IRVM is a Rust library providing an intermediate representation (IR) that lowers to LLVM IR. It offers a type-safe, Rust-friendly API for generating LLVM IR without direct FFI usage.
Target LLVM version: 20 (llvm-sys 201.0.1)
irvm/
├── src/ # Core IR library
│ ├── lib.rs # Public API exports
│ ├── module.rs # Module, GlobalVariable, GlobalIdx
│ ├── function.rs # Function, Parameter, FnIdx
│ ├── block.rs # Block, Instructions, Terminators (~900 lines)
│ ├── types.rs # Type system, TypeStorage, TypeIdx
│ ├── value.rs # Operand, ConstValue
│ ├── common.rs # Linkage, Visibility, CConv, etc.
│ ├── datalayout.rs # DataLayout for type sizes/alignment
│ └── error.rs # Error types
├── irvm-lower/ # LLVM lowering crate
│ └── src/
│ ├── lib.rs # Public exports
│ └── llvm.rs # Lowering implementation (~2500 lines)
├── examples/simple/ # Example usage
├── ROADMAP.md # Feature roadmap and coverage estimates
└── Cargo.toml # Workspace root
cargo build --workspace # Build all crates
cargo test --workspace # Run all tests
cargo clippy --workspace # LintAll indices use typed_generational_arena::StandardSlabIndex:
TypeIdx- index intoTypeStorageBlockIdx- index into function's blocksInstIdx- index into block's instructionsFnIdx- index into module's functionsGlobalIdx- index into module's globals
Instructions are added via methods on Block:
block.instr_add(lhs, rhs, location) // Returns Operand
block.instr_store(value, ptr, location) // Returns ()
block.set_terminator(Terminator::Ret(...)) // Sets block terminatorIn irvm-lower/src/llvm.rs, lowering happens in two passes:
- Declaration pass: Create LLVM function/global declarations
- Body pass: Lower instructions with all references available
Values are represented as Operand enum:
Parameter(nth, type)- function parameterBlockArgument { block_idx, nth, ty }- phi-like block argumentValue(block, inst, type)- result of an instructionConstant(ConstValue, type)- compile-time constantGlobal(GlobalIdx, type)- reference to global variable
In src/block.rs, instructions are organized by enum:
BinaryOp- add, sub, mul, div, rem (int)BitwiseBinaryOp- and, or, xor, shl, lshr, ashrConversionOp- trunc, zext, sext, fptrunc, fpext, castsVectorOp- extractelement, insertelement, shufflevectorAggregateOp- extractvalue, insertvalueMemoryOp- load, store, alloca, GEP, atomics, fenceOtherOp- icmp, fcmp, call, phi, select, landingpadDebugOp- debug value/declare
Terminators in Terminator enum:
Ret,Br,CondBr,Switch,Invoke,Resume,Unreachable
- Add variant to appropriate enum in
src/block.rs - Add
instr_*method toBlockimpl - Add lowering in
irvm-lower/src/llvm.rsin the instruction match
- Add variant to
Typeenum insrc/types.rs - Add
get_or_create_*method toTypeStorage - Add lowering in
lower_type()function inllvm.rs
The test functions use LLVMDumpModule or LLVMPrintModuleToString to print generated IR. Check irvm-lower/src/lib.rs tests for examples.
// Before using icmp (which returns i1), ensure i1 type exists:
let _i1 = storage.get_or_create_i1();
// Similarly for i64:
let _i64 = storage.get_or_create_i64();Types don't require debug names - anonymous debug types are auto-generated:
// Both work fine:
storage.add_type(Type::Int(32), Some("i32")); // Named
storage.add_type(Type::Int(32), None); // AnonymousThe MCJIT execution engine only supports certain parameter/return types reliably:
- Use
i32parameters and returns for JIT tests - Avoid
i8,i16, structs as function parameters in JIT tests - For testing small types, use
i32params and trunc/ext internally
- Use
LLVMConstArray2(not deprecatedLLVMConstArray) - Use typed APIs:
LLVMBuildLoad2,LLVMBuildGEP2,LLVMBuildCall2,LLVMBuildInvoke2 LLVMSetFastMathFlagsis available for applying fast-math flags- Opaque pointers are the default (no typed pointers)
~65-70% of LLVM IR (v0.2.0). See ROADMAP.md for details.
Implemented:
- All arithmetic/bitwise operations
- Type casts (trunc, zext, sext, fp casts, ptr↔int, bitcast)
- Memory operations (load, store, alloca, GEP)
- Atomic operations (atomicrmw, cmpxchg, fence)
- Control flow (br, condbr, switch, ret)
- Exception handling (invoke, landingpad, resume, unreachable)
- Vector operations (extract/insert element, shufflevector)
- Aggregate operations (extractvalue, insertvalue)
- Global variables with full attributes
- Fast-math flags
- Debug info (DISubprogram, DILocation, DILocalVariable, etc.)
Not yet implemented (see ROADMAP.md):
- Intrinsics system
- Function/parameter attributes
- GEP type inference
- Module metadata
- Inline assembly
- Edition 2024
- No unsafe in
irvmcrate (all unsafe inirvm-lower) - Prefer explicit types over inference for public APIs
- Use
Locationfor source mapping on all constructs - Clone-friendly types (most types derive Clone)
- Use
cargo fmtafter changes