# How to Use LLVM's C API (LLVM-C) to Build Custom Tools: A Complete Guide

> Master LLVM's C API (LLVM-C) to build custom tools. This guide covers IR generation, JIT compilation, and code execution using key LLVM-C functions. Start building now!

- Repository: [LLVM/llvm-project](https://github.com/llvm/llvm-project)
- Tags: how-to-guide
- Published: 2026-09-11

---

**LLVM's C API (LLVM-C) provides stable, opaque C bindings to LLVM's C++ core, enabling you to generate IR, create JIT compilers, and execute code using functions like `LLVMContextCreate`, `LLVMBuildAdd`, and `LLVMCreateJITCompilerForModule`.**

LLVM's C API (LLVM-C) is a thin, binary-compatible wrapper around the core LLVM C++ libraries, residing in the `llvm/llvm-project` repository. It exposes compiler infrastructure—including contexts, modules, type systems, and execution engines—through plain C functions and opaque handles. This design allows developers to build custom LLVM-based tools in C, Rust, Python (via ctypes), or any language supporting C FFI, without linking against the C++ standard library.

## Core Architectural Components

The LLVM-C interface centers on **opaque reference types** that isolate LLVM objects across API boundaries. Each component maps to a specific header in `llvm/include/llvm-c/`:

- **`LLVMContextRef`** – Represents [`llvm/include/llvm-c/Context.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Context.h). Holds uniqued objects like types and constants, isolating independent compilations.
- **`LLVMModuleRef`** – Defined in [`llvm/include/llvm-c/Core.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Core.h). Acts as the top-level container for functions, globals, and metadata, analogous to a translation unit.
- **`LLVMTypeRef`** and **`LLVMValueRef`** – Both declared in [`llvm/include/llvm-c/Core.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Core.h). Describe value types (integers, floats, structs) and represent instructions, arguments, and constants respectively.
- **IR Builder Functions** – Declared in [`llvm/include/llvm-c/IRBuilder.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/IRBuilder.h). Provide instruction constructors like `LLVMBuildAdd` and `LLVMBuildRet` for appending operations to basic blocks.
- **`LLVMExecutionEngineRef`** – Found in [`llvm/include/llvm-c/ExecutionEngine.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/ExecutionEngine.h). Enables JIT compilation and runtime function invocation.
- **Target Configuration** – [`llvm/include/llvm-c/Target.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Target.h) exposes `LLVMTargetRef` and initialization routines like `LLVMInitializeNativeTarget`.

All handles are opaque pointers, guaranteeing binary compatibility across LLVM releases when linking against `libLLVM-C`.

## Step-by-Step Implementation Workflow

Building a custom tool with LLVM-C follows a predictable lifecycle. Each stage utilizes specific functions from the source headers:

1. **Initialize the Native Target** – Call `LLVMInitializeNativeTarget()` and `LLVMInitializeNativeAsmPrinter()` (from [`llvm/include/llvm-c/Target.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Target.h)) to register the host CPU for JIT compilation.

2. **Create a Context** – Invoke `LLVMContextCreate()` to generate an `LLVMContextRef`. All subsequent type and value creation must reference this context.

3. **Allocate a Module** – Use `LLVMModuleCreateWithNameInContext()` to produce an `LLVMModuleRef`, specifying the context from step two.

4. **Construct Types and Functions** – Build function signatures using `LLVMInt32TypeInContext()` and `LLVMFunctionType()`, then insert functions into the module with `LLVMAddFunction()`.

5. **Generate IR** – Append basic blocks with `LLVMAppendBasicBlock()`, position an `LLVMBuilderRef` using `LLVMCreateBuilderInContext()` and `LLVMPositionBuilderAtEnd()`, then emit instructions via builder functions.

6. **Verify the Module** – Pass the module to `LLVMVerifyModule()` (declared in [`llvm/include/llvm-c/Analysis.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Analysis.h)) with `LLVMAbortProcessAction` to catch malformed IR before execution.

7. **Instantiate the Execution Engine** – Create a JIT compiler using `LLVMCreateJITCompilerForModule()`, which produces an `LLVMExecutionEngineRef` capable of emitting machine code.

8. **Execute and Cleanup** – Run functions with `LLVMRunFunction()`, passing `LLVMGenericValueRef` arguments created via `LLVMCreateGenericValueOfInt()`. Release resources using `LLVMDisposeExecutionEngine()`, `LLVMDisposeModule()`, and `LLVMContextDispose()`.

## Complete Working Example: JIT-Compiling an Add Function

The following self-contained example creates a module containing `add(i32, i32) -> i32`, JIT-compiles it, and invokes the function from C. It demonstrates the essential API calls required for custom tool development.

```c
/* compile:  gcc -g -O2 example.c `llvm-config --cflags --libs --system-libs` -lLLVM-C -o example */

#include <stdio.h>
#include <stdlib.h>
#include "llvm-c/Core.h"
#include "llvm-c/ExecutionEngine.h"
#include "llvm-c/Target.h"
#include "llvm-c/Analysis.h"

int main(void) {
    /* 1. Initialise the native target (required for JIT). */
    LLVMInitializeNativeTarget();
    LLVMInitializeNativeAsmPrinter();

    /* 2. Context & Module. */
    LLVMContextRef ctx = LLVMContextCreate();
    LLVMModuleRef mod  = LLVMModuleCreateWithNameInContext("my_module", ctx);

    /* 3. Types. */
    LLVMTypeRef i32 = LLVMInt32TypeInContext(ctx);
    LLVMTypeRef param_types[] = { i32, i32 };
    LLVMTypeRef fn_type = LLVMFunctionType(i32, param_types, 2, 0);

    /* 4. Function and basic block. */
    LLVMValueRef add_fn = LLVMAddFunction(mod, "add", fn_type);
    LLVMSetFunctionCallConv(add_fn, LLVMCCallConv);   /* optional */

    LLVMBasicBlockRef entry = LLVMAppendBasicBlock(add_fn, "entry");
    LLVMBuilderRef builder = LLVMCreateBuilderInContext(ctx);
    LLVMPositionBuilderAtEnd(builder, entry);

    /* 5. Build body: %a = param0, %b = param1, %sum = add %a, %b, ret %sum */
    LLVMValueRef a = LLVMGetParam(add_fn, 0);
    LLVMValueRef b = LLVMGetParam(add_fn, 1);
    LLVMValueRef sum = LLVMBuildAdd(builder, a, b, "sum");
    LLVMBuildRet(builder, sum);

    /* 6. Verify & optionally dump. */
    char *error = NULL;
    if (LLVMVerifyModule(mod, LLVMAbortProcessAction, &error)) {
        fprintf(stderr, "Module verification failed: %s\n", error);
        LLVMDisposeMessage(error);
        exit(1);
    }
    // LLVMDumpModule(mod);   /* useful for debugging */

    /* 7. Create JIT execution engine. */
    LLVMExecutionEngineRef engine;
    if (LLVMCreateJITCompilerForModule(&engine, mod, 0, &error)) {
        fprintf(stderr, "Failed to create JIT: %s\n", error);
        LLVMDisposeMessage(error);
        exit(1);
    }

    /* 8. Run the compiled function. */
    LLVMGenericValueRef args[2];
    args[0] = LLVMCreateGenericValueOfInt(i32, 40, 0);
    args[1] = LLVMCreateGenericValueOfInt(i32, 2, 0);
    LLVMGenericValueRef ret = LLVMRunFunction(engine, add_fn, 2, args);
    unsigned long long result = LLVMGenericValueToInt(ret, 0);
    printf("add(40, 2) = %llu\n", result);   /* prints 42 */

    /* 9. Clean‑up. */
    LLVMDisposeGenericValue(args[0]);
    LLVMDisposeGenericValue(args[1]);
    LLVMDisposeGenericValue(ret);
    LLVMDisposeBuilder(builder);
    LLVMDisposeExecutionEngine(engine);
    LLVMContextDispose(ctx);
    return 0;
}

```

This example links against `libLLVM-C` using `llvm-config` to resolve flags. The `LLVMBuildAdd` and `LLVMBuildRet` functions, though used here, are formally declared in [`llvm/include/llvm-c/IRBuilder.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/IRBuilder.h), which you should include for complex IR generation.

## Essential Header Files for Custom Tools

When developing with LLVM-C, include these headers based on functionality:

- **[`llvm/include/llvm-c/Core.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Core.h)** – Context creation, module management, type construction, and value manipulation.
- **[`llvm/include/llvm-c/IRBuilder.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/IRBuilder.h)** – Instruction building utilities like `LLVMBuildAdd` and `LLVMBuildRet`.
- **[`llvm/include/llvm-c/ExecutionEngine.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/ExecutionEngine.h)** – JIT compiler creation (`LLVMCreateJITCompilerForModule`) and function invocation (`LLVMRunFunction`).
- **[`llvm/include/llvm-c/Target.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Target.h)** – Target initialization (`LLVMInitializeNativeTarget`) and target data layout.
- **[`llvm/include/llvm-c/TargetMachine.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/TargetMachine.h)** – Fine-grained code-generation configuration for MCJIT.
- **[`llvm/include/llvm-c/Analysis.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Analysis.h)** – Module verification via `LLVMVerifyModule`.
- **[`llvm/include/llvm-c/Types.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Types.h)** – Opaque type definitions (`LLVMContextRef`, `LLVMModuleRef`, `LLVMValueRef`).

## Summary

- LLVM's C API provides **stable, opaque handles** (`LLVMContextRef`, `LLVMModuleRef`) that insulate your tool from C++ ABI changes.
- The standard workflow requires **initializing targets**, creating a **context** and **module**, building IR with **type constructors** and **builder functions**, then instantiating an **execution engine**.
- **Memory management** is explicit: every `LLVMCreate*` or `LLVM*Create*` call requires a corresponding `LLVMDispose*` call to prevent leaks.
- Key headers reside in `llvm/include/llvm-c/`, with [`Core.h`](https://github.com/llvm/llvm-project/blob/main/Core.h), [`IRBuilder.h`](https://github.com/llvm/llvm-project/blob/main/IRBuilder.h), and [`ExecutionEngine.h`](https://github.com/llvm/llvm-project/blob/main/ExecutionEngine.h) providing the majority of functionality for custom JIT tools.

## Frequently Asked Questions

### How do I initialize LLVM for JIT compilation using the C API?

You must call `LLVMInitializeNativeTarget()` and `LLVMInitializeNativeAsmPrinter()` from [`llvm/include/llvm-c/Target.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Target.h) before creating a JIT execution engine. These functions register the host architecture with the LLVM backend, enabling `LLVMCreateJITCompilerForModule()` to generate machine code for the local CPU.

### What is the difference between `LLVMCreateJITCompilerForModule` and `LLVMCreateInterpreterForModule`?

`LLVMCreateJITCompilerForModule` (declared in [`llvm/include/llvm-c/ExecutionEngine.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/ExecutionEngine.h)) compiles LLVM IR to native machine code for fast execution, while `LLVMCreateInterpreterForModule` executes IR instruction-by-instruction without compilation, sacrificing performance for debugging flexibility. Most custom tools use the JIT compiler for production workloads.

### How do I prevent memory leaks when using LLVM-C opaque handles?

Every creation function requires explicit disposal: call `LLVMDisposeExecutionEngine()` for engines, `LLVMDisposeModule()` for modules, `LLVMDisposeBuilder()` for builders, and `LLVMContextDispose()` for contexts. Additionally, `LLVMDisposeMessage()` frees error strings returned by functions like `LLVMVerifyModule`.

### Can LLVM-C generate object files for targets other than the host?

Yes. While the example uses `LLVMInitializeNativeTarget()` for convenience, you can initialize specific targets using `LLVMInitializeX86Target()`, `LLVMInitializeARMTarget()`, or similar architecture-specific functions from [`llvm/include/llvm-c/Target.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/Target.h). Combined with `LLVMTargetMachineRef` from [`llvm/include/llvm-c/TargetMachine.h`](https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm-c/TargetMachine.h), you can configure cross-compilation settings, CPUs, and feature sets before emitting object code via the `LLVMTargetMachineEmitToFile` interface.