inkwell
The inkwell
crate provides safe, idiomatic Rust bindings to LLVM, enabling compiler developers to generate highly optimized machine code while leveraging LLVM’s mature optimization infrastructure and broad platform support. LLVM IR serves as a universal intermediate representation that can be compiled to native code for virtually any modern processor architecture. The inkwell bindings wrap LLVM’s C++ API with Rust’s type system and ownership model, preventing common errors like use-after-free and type mismatches that plague direct LLVM usage.
The architecture of inkwell mirrors LLVM’s conceptual model while providing Rust-native abstractions. Contexts manage the lifetime of LLVM data structures, modules contain functions and global variables, builders construct instruction sequences, and execution engines provide JIT compilation capabilities. This design ensures memory safety through Rust’s lifetime system while maintaining the full power and flexibility of LLVM’s code generation capabilities.
Installation and Setup
Inkwell currently supports LLVM versions 8 through 18. You must have LLVM installed on your system and specify the version in your Cargo.toml
dependencies.
On macOS, you can use Homebrew to install LLVM. For example, to install LLVM 18:
brew install llvm@18
Add inkwell to your Cargo.toml
with the appropriate LLVM version feature flag:
[dependencies]
inkwell = { version = "0.6.0", features = ["llvm18-1"] }
Supported versions use the pattern llvmM-0
where M is the LLVM major version (8-18).
Basic Usage
Creating an LLVM context for code generation:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } }
The context is the core of LLVM’s infrastructure, owning all types, values, and metadata. Every LLVM operation requires a context, which manages memory and ensures proper cleanup of LLVM data structures.
Function Creation
Building simple arithmetic functions:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } }
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } }
Function creation involves defining the function signature through LLVM’s type system, adding the function to the module, and creating basic blocks for the function body. The entry block serves as the starting point for instruction generation. Parameters are accessed through the function value and can be used in subsequent instructions.
Constants and Comparisons
Creating constant values and comparison operations:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } }
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } }
Constants are compile-time values that LLVM can optimize aggressively. Comparison operations produce boolean results used for control flow decisions. LLVM supports both signed and unsigned integer comparisons with various predicates.
Control Flow
Implementing conditional branches and phi nodes:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } }
Control flow in LLVM uses explicit basic blocks connected by branch instructions. Conditional branches test a boolean condition and jump to one of two target blocks. Phi nodes implement the SSA form by selecting values based on the predecessor block. This explicit representation enables sophisticated control flow optimizations.
Loop Construction
Building loops with phi nodes:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } }
Loops in LLVM use phi nodes to manage loop variables in SSA form. The loop structure consists of an entry block, a loop block containing the phi node and loop body, and an exit block. The phi node receives different values depending on whether control flow comes from the initial entry or from the loop itself.
Stack Allocation
Using alloca for local variables:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } }
The alloca instruction creates stack storage for mutable variables. Load and store instructions access these variables. This pattern is commonly used before mem2reg optimization, which promotes allocas to SSA registers when possible.
Array Operations
Working with arrays and pointers:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } }
Array operations use the GEP (GetElementPtr) instruction to compute addresses of array elements. The GEP instruction performs pointer arithmetic in a type-safe manner, taking into account element sizes and array dimensions.
Structure Types
Defining and manipulating aggregate types:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } }
Structures in LLVM represent aggregate data types with indexed fields. The extract_value instruction retrieves fields from struct values. This example shows how to work with heterogeneous data types in LLVM.
Global Variables
Creating and using module-level variables:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } }
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } }
Global variables exist at module scope and can be accessed by all functions. They support various linkage types controlling visibility and sharing across compilation units.
Recursive Functions
Implementing recursion:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } }
Recursive functions in LLVM work like any other function call. The function can call itself by using its own function value as the callee. This example implements factorial recursively with a base case and recursive case.
Optimization
Applying LLVM’s optimization passes using the modern pass manager:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } }
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } }
LLVM provides a modern pass manager (available in LLVM 18) with a string-based interface for specifying optimization pipelines. Common passes include instcombine, reassociate, gvn, simplifycfg, and mem2reg. The PassBuilderOptions allows fine-grained control over optimization behavior.
JIT Compilation
Just-in-time compilation for immediate execution:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } }
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } }
The execution engine provides JIT compilation capabilities, compiling LLVM IR to machine code in memory for immediate execution. This enables dynamic code generation scenarios like REPLs, runtime specialization, and adaptive optimization.
Code Emission
Generating object files and LLVM IR:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } }
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } }
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } }
LLVM can emit code in various formats including object files and LLVM IR text. The target machine encapsulates platform-specific code generation details. Module verification ensures the generated IR is well-formed before optimization or code generation.
Helper Functions
Utility for creating function types:
#![allow(unused)] fn main() { use std::error::Error; use std::path::Path; use inkwell::context::Context; use inkwell::module::Module; use inkwell::passes::PassBuilderOptions; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum}; use inkwell::values::FunctionValue; use inkwell::{AddressSpace, IntPredicate, OptimizationLevel}; /// Creates a basic LLVM context pub fn create_context() -> Context { Context::create() } /// Creates a simple function that adds two integers pub fn create_add_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("add", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get function parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Build addition and return let sum = builder.build_int_add(x, y, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); function } /// Creates a function that multiplies two integers pub fn create_multiply_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("multiply", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let product = builder.build_int_mul(x, y, "product").unwrap(); builder.build_return(Some(&product)).unwrap(); function } /// Creates a function with a constant return value pub fn create_constant_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("get_constant", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let constant = i32_type.const_int(42, false); builder.build_return(Some(&constant)).unwrap(); function } /// Demonstrates integer comparison operations pub fn create_comparison_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let bool_type = context.bool_type(); let fn_type = bool_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("compare_ints", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Compare x > y let comparison = builder .build_int_compare(IntPredicate::SGT, x, y, "cmp") .unwrap(); builder.build_return(Some(&comparison)).unwrap(); function } /// Creates a function with conditional branching (if-then-else) pub fn create_conditional_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("conditional", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let then_block = context.append_basic_block(function, "then"); let else_block = context.append_basic_block(function, "else"); let merge_block = context.append_basic_block(function, "merge"); // Entry block builder.position_at_end(entry); let x = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); let condition = builder .build_int_compare(IntPredicate::SGT, x, zero, "cond") .unwrap(); builder .build_conditional_branch(condition, then_block, else_block) .unwrap(); // Then block: return x * 2 builder.position_at_end(then_block); let two = i32_type.const_int(2, false); let then_val = builder.build_int_mul(x, two, "then_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Else block: return x * -1 builder.position_at_end(else_block); let neg_one = i32_type.const_int(-1i64 as u64, true); let else_val = builder.build_int_mul(x, neg_one, "else_val").unwrap(); builder.build_unconditional_branch(merge_block).unwrap(); // Merge block with phi node builder.position_at_end(merge_block); let phi = builder.build_phi(i32_type, "result").unwrap(); phi.add_incoming(&[(&then_val, then_block), (&else_val, else_block)]); builder.build_return(Some(&phi.as_basic_value())).unwrap(); function } /// Creates a simple loop that counts from 0 to n pub fn create_loop_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("count_loop", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block: initialize counter builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let counter = builder.build_phi(i32_type, "counter").unwrap(); counter.add_incoming(&[(&zero, entry)]); // Increment counter let one = i32_type.const_int(1, false); let next_counter = builder .build_int_add( counter.as_basic_value().into_int_value(), one, "next_counter", ) .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_counter, n, "loop_cond") .unwrap(); // Add incoming value for next iteration let loop_block_end = builder.get_insert_block().unwrap(); counter.add_incoming(&[(&next_counter, loop_block_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder .build_return(Some(&counter.as_basic_value())) .unwrap(); function } /// Creates a function that allocates and uses local variables (stack /// allocation) pub fn create_alloca_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into(), i32_type.into()], false); let function = module.add_function("use_alloca", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get parameters let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); // Allocate stack variables let x_ptr = builder.build_alloca(i32_type, "x_ptr").unwrap(); let y_ptr = builder.build_alloca(i32_type, "y_ptr").unwrap(); let result_ptr = builder.build_alloca(i32_type, "result_ptr").unwrap(); // Store values builder.build_store(x_ptr, x).unwrap(); builder.build_store(y_ptr, y).unwrap(); // Load values let x_val = builder.build_load(i32_type, x_ptr, "x_val").unwrap(); let y_val = builder.build_load(i32_type, y_ptr, "y_val").unwrap(); // Compute and store result let sum = builder .build_int_add(x_val.into_int_value(), y_val.into_int_value(), "sum") .unwrap(); builder.build_store(result_ptr, sum).unwrap(); // Load and return result let result = builder.build_load(i32_type, result_ptr, "result").unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a function that works with arrays pub fn create_array_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let i32_ptr_type = context.ptr_type(AddressSpace::default()); let fn_type = i32_type.fn_type(&[i32_ptr_type.into(), i32_type.into()], false); let function = module.add_function("sum_array", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let loop_block = context.append_basic_block(function, "loop"); let exit_block = context.append_basic_block(function, "exit"); // Entry block builder.position_at_end(entry); let array_ptr = function.get_nth_param(0).unwrap().into_pointer_value(); let size = function.get_nth_param(1).unwrap().into_int_value(); let zero = i32_type.const_int(0, false); builder.build_unconditional_branch(loop_block).unwrap(); // Loop block builder.position_at_end(loop_block); let index = builder.build_phi(i32_type, "index").unwrap(); let sum = builder.build_phi(i32_type, "sum").unwrap(); index.add_incoming(&[(&zero, entry)]); sum.add_incoming(&[(&zero, entry)]); // Load array element let elem_ptr = unsafe { builder .build_gep( i32_type, array_ptr, &[index.as_basic_value().into_int_value()], "elem_ptr", ) .unwrap() }; let elem = builder.build_load(i32_type, elem_ptr, "elem").unwrap(); // Update sum let new_sum = builder .build_int_add( sum.as_basic_value().into_int_value(), elem.into_int_value(), "new_sum", ) .unwrap(); // Update index let one = i32_type.const_int(1, false); let next_index = builder .build_int_add(index.as_basic_value().into_int_value(), one, "next_index") .unwrap(); // Check loop condition let condition = builder .build_int_compare(IntPredicate::SLT, next_index, size, "loop_cond") .unwrap(); // Update phi nodes let loop_end = builder.get_insert_block().unwrap(); index.add_incoming(&[(&next_index, loop_end)]); sum.add_incoming(&[(&new_sum, loop_end)]); builder .build_conditional_branch(condition, loop_block, exit_block) .unwrap(); // Exit block builder.position_at_end(exit_block); builder.build_return(Some(&sum.as_basic_value())).unwrap(); function } /// Creates a global variable pub fn create_global_variable<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> inkwell::values::GlobalValue<'ctx> { let i32_type = context.i32_type(); let global = module.add_global(i32_type, Some(AddressSpace::default()), "global_counter"); global.set_initializer(&i32_type.const_int(0, false)); global.set_linkage(inkwell::module::Linkage::Internal); global } /// Creates a function that uses a global variable pub fn create_global_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[], false); let function = module.add_function("increment_global", fn_type, None); // Create or get global variable let global = module .get_global("global_counter") .unwrap_or_else(|| create_global_variable(context, module)); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Load global value let global_ptr = global.as_pointer_value(); let current = builder.build_load(i32_type, global_ptr, "current").unwrap(); // Increment let one = i32_type.const_int(1, false); let incremented = builder .build_int_add(current.into_int_value(), one, "incremented") .unwrap(); // Store back to global builder.build_store(global_ptr, incremented).unwrap(); // Return new value builder.build_return(Some(&incremented)).unwrap(); function } /// Creates a recursive factorial function pub fn create_recursive_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let fn_type = i32_type.fn_type(&[i32_type.into()], false); let function = module.add_function("factorial", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); let recurse = context.append_basic_block(function, "recurse"); let base = context.append_basic_block(function, "base"); // Entry block builder.position_at_end(entry); let n = function.get_nth_param(0).unwrap().into_int_value(); let one = i32_type.const_int(1, false); let is_base = builder .build_int_compare(IntPredicate::SLE, n, one, "is_base") .unwrap(); builder .build_conditional_branch(is_base, base, recurse) .unwrap(); // Base case: return 1 builder.position_at_end(base); builder.build_return(Some(&one)).unwrap(); // Recursive case: return n * factorial(n-1) builder.position_at_end(recurse); let n_minus_1 = builder.build_int_sub(n, one, "n_minus_1").unwrap(); let rec_result = builder .build_call(function, &[n_minus_1.into()], "rec_result") .unwrap(); let result = builder .build_int_mul( n, rec_result .try_as_basic_value() .left() .unwrap() .into_int_value(), "result", ) .unwrap(); builder.build_return(Some(&result)).unwrap(); function } /// Creates a struct type and a function that uses it pub fn create_struct_function<'ctx>( context: &'ctx Context, module: &Module<'ctx>, ) -> FunctionValue<'ctx> { let i32_type = context.i32_type(); let f64_type = context.f64_type(); // Define a Point struct with x and y fields let field_types = [i32_type.into(), f64_type.into()]; let struct_type = context.struct_type(&field_types, false); let fn_type = f64_type.fn_type(&[struct_type.into()], false); let function = module.add_function("get_point_y", fn_type, None); let builder = context.create_builder(); let entry = context.append_basic_block(function, "entry"); builder.position_at_end(entry); // Get the struct parameter let point = function.get_nth_param(0).unwrap().into_struct_value(); // Extract the y field (index 1) let y_field = builder.build_extract_value(point, 1, "y_field").unwrap(); builder.build_return(Some(&y_field)).unwrap(); function } /// Runs optimization passes on a module using the modern pass manager (LLVM 18) pub fn optimize_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { // First verify the module is valid module.verify().map_err(|e| e.to_string())?; // Initialize targets for optimization Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; // Common optimization passes let passes = [ "instcombine", // Combine instructions "reassociate", // Reassociate expressions "gvn", // Global value numbering "simplifycfg", // Simplify control flow graph "mem2reg", // Promote memory to registers ]; let pass_builder_options = PassBuilderOptions::create(); pass_builder_options.set_loop_vectorization(true); pass_builder_options.set_loop_unrolling(true); pass_builder_options.set_merge_functions(true); module .run_passes(&passes.join(","), &target_machine, pass_builder_options) .map_err(|e| e.to_string()) } /// Runs specific optimization passes on a module pub fn run_custom_passes<'ctx>(module: &Module<'ctx>, passes: &[&str]) -> Result<(), String> { // Verify module first module.verify().map_err(|e| e.to_string())?; // Initialize targets Target::initialize_all(&InitializationConfig::default()); let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple) .map_err(|e| format!("Failed to create target: {}", e))?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::None, RelocMode::Default, CodeModel::Default, ) .ok_or("Failed to create target machine")?; module .run_passes( &passes.join(","), &target_machine, PassBuilderOptions::create(), ) .map_err(|e| e.to_string()) } /// Writes LLVM IR to a file pub fn write_ir_to_file<'ctx>(module: &Module<'ctx>, path: &Path) -> Result<(), String> { module .print_to_file(path) .map_err(|e| format!("Failed to write IR: {}", e)) } /// Compiles module to object file pub fn compile_to_object_file<'ctx>( module: &Module<'ctx>, path: &Path, ) -> Result<(), Box<dyn Error>> { Target::initialize_native(&InitializationConfig::default())?; let target_triple = TargetMachine::get_default_triple(); let target = Target::from_triple(&target_triple)?; let target_machine = target .create_target_machine( &target_triple, "generic", "", OptimizationLevel::Default, RelocMode::Default, CodeModel::Default, ) .ok_or("Could not create target machine")?; target_machine.write_to_file(module, FileType::Object, path)?; Ok(()) } /// Verifies that a module is valid pub fn verify_module<'ctx>(module: &Module<'ctx>) -> Result<(), String> { module.verify().map_err(|e| e.to_string()) } /// Simple JIT execution example pub fn create_execution_engine<'ctx>( module: &Module<'ctx>, ) -> Result<inkwell::execution_engine::ExecutionEngine<'ctx>, String> { module .create_jit_execution_engine(OptimizationLevel::None) .map_err(|e| e.to_string()) } /// Example of JIT compiling and executing a function pub fn jit_compile_and_execute(context: &Context) -> Result<u64, Box<dyn Error>> { let module = context.create_module("jit_example"); let builder = context.create_builder(); // Create a simple sum function: sum(x, y, z) = x + y + z let i64_type = context.i64_type(); let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false); let function = module.add_function("sum", fn_type, None); let basic_block = context.append_basic_block(function, "entry"); builder.position_at_end(basic_block); let x = function.get_nth_param(0).unwrap().into_int_value(); let y = function.get_nth_param(1).unwrap().into_int_value(); let z = function.get_nth_param(2).unwrap().into_int_value(); let sum = builder.build_int_add(x, y, "sum").unwrap(); let sum = builder.build_int_add(sum, z, "sum").unwrap(); builder.build_return(Some(&sum)).unwrap(); // Create execution engine let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?; // Get the compiled function type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64; let sum_fn = unsafe { execution_engine.get_function::<SumFunc>("sum")? }; // Execute the function let result = unsafe { sum_fn.call(1, 2, 3) }; Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn test_add_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_add_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "add"); assert_eq!(function.count_params(), 2); assert!(verify_module(&module).is_ok()); } #[test] fn test_constant_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_constant_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "get_constant"); assert_eq!(function.count_params(), 0); assert!(verify_module(&module).is_ok()); } #[test] fn test_conditional_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_conditional_function(&context, &module); assert_eq!(function.count_basic_blocks(), 4); // entry, then, else, merge assert!(verify_module(&module).is_ok()); } #[test] fn test_loop_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_loop_function(&context, &module); assert_eq!(function.count_basic_blocks(), 3); // entry, loop, exit assert!(verify_module(&module).is_ok()); } #[test] fn test_global_variable() { let context = Context::create(); let module = context.create_module("test"); let global = create_global_variable(&context, &module); assert_eq!(global.get_name().to_str().unwrap(), "global_counter"); assert!(verify_module(&module).is_ok()); } #[test] fn test_recursive_function() { let context = Context::create(); let module = context.create_module("test"); let function = create_recursive_function(&context, &module); assert_eq!(function.get_name().to_str().unwrap(), "factorial"); assert!(verify_module(&module).is_ok()); } #[test] fn test_optimization() { let context = Context::create(); let module = context.create_module("test"); // Create several functions create_add_function(&context, &module); create_multiply_function(&context, &module); create_constant_function(&context, &module); // Apply optimizations assert!(optimize_module(&module).is_ok()); // Module should still be valid after optimization assert!(verify_module(&module).is_ok()); } #[test] fn test_custom_passes() { let context = Context::create(); let module = context.create_module("test"); // Create a simple function create_add_function(&context, &module); // Run specific optimization passes let passes = ["instcombine", "simplifycfg"]; assert!(run_custom_passes(&module, &passes).is_ok()); // Module should still be valid assert!(verify_module(&module).is_ok()); } #[test] fn test_jit_execution() { let context = Context::create(); // Test JIT compilation and execution match jit_compile_and_execute(&context) { Ok(result) => assert_eq!(result, 6), // 1 + 2 + 3 = 6 Err(e) => panic!("JIT execution failed: {}", e), } } } /// Helper to create a function type pub fn create_function_type<'ctx>( context: &'ctx Context, param_types: Vec<BasicMetadataTypeEnum<'ctx>>, return_type: Option<BasicTypeEnum<'ctx>>, is_var_args: bool, ) -> inkwell::types::FunctionType<'ctx> { match return_type { Some(ret) => ret.fn_type(¶m_types, is_var_args), None => context.void_type().fn_type(¶m_types, is_var_args), } } }
This helper simplifies creating function types with proper handling of void returns and variadic arguments.
Best Practices
Maintain clear separation between your language’s AST and LLVM IR generation. Build an intermediate representation that bridges your language semantics and LLVM’s model. This separation simplifies both frontend development and backend optimization.
Use LLVM’s type system to enforce invariants at compile time. Rich type information enables better optimization and catches errors early. Avoid using opaque pointers when specific types provide better optimization opportunities.
Leverage LLVM’s SSA form by minimizing mutable state. Use phi nodes instead of memory operations when possible. SSA form enables powerful optimizations like constant propagation and dead code elimination.
Structure code generation to emit IR suitable for optimization. Avoid patterns that inhibit optimization like excessive memory operations or complex control flow. Simple, regular IR patterns optimize better than clever, complicated constructions.
Enable appropriate optimization levels based on use case. Debug builds benefit from minimal optimization for faster compilation and better debugging. Release builds should use higher optimization levels for maximum performance.
Use LLVM intrinsics for operations with hardware support. Intrinsics for mathematical functions, atomic operations, and SIMD instructions generate better code than manual implementations. LLVM recognizes and optimizes intrinsic patterns.
Profile and analyze generated code to identify optimization opportunities. LLVM provides extensive analysis passes that reveal performance bottlenecks. Use this information to guide both frontend improvements and optimization pass selection.