# LLVMSharp: generated .NET bindings over the LLVM C API

> LLVMSharp is a .NET Standard library that exposes the LLVM infrastructure to C# by auto-generating wrappers from the LLVM C headers with ClangSharp. It is aimed at .NET developers who need to build a compiler, an analyser or a code generator, and it is deliberately shaped like the C API rather than like an idiomatic .NET surface.

**dotnet/LLVMSharp** — LLVM bindings for .NET Standard written in C# using ClangSharp

- Repository: https://github.com/dotnet/LLVMSharp
- Stars: 951 · Forks: 112
- Language: C#
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/dotnet-llvmsharp

## A binding layer for people who need LLVM from C#

LLVMSharp is a multi-platform .NET Standard library for accessing the LLVM infrastructure. The problem it solves is narrow and unglamorous: LLVM is a C++ project whose public surface is a C API, and a .NET developer who wants to build a compiler, a code generator or a static analysis tool has no supported way to reach it from C#. LLVMSharp fills that gap with generated wrappers.

The audience is therefore a specific group. If you are writing a .NET application that needs to emit LLVM IR, or a tool that reuses LLVM's optimisation passes, this is the piece that connects the two worlds. It is not a general purpose library for .NET developers, and it does not try to be. The README says the bindings support all functionality exposed by the LLVM C headers, and describes that as more than enough to build a full compiler, which is the intended ceiling of ambition.

Packaging is straightforward for a native interop project. The managed library is published to NuGet as llvmsharp, and a separate convenience package, libLLVM, provides the native libLLVM library for several platforms. Source browsing is offered through a separate hosted view, which matters for a generated codebase where reading the source is how you check what a particular call actually does.

## The bindings are generated from the LLVM C headers, not written by hand

The architectural decision that defines this project is that the wrappers are auto-generated. The README states that the bindings are auto-generated using ClangSharp parsing the LLVM C header files. ClangSharp generates bindings by parsing the LLVM C headers, and using it means the managed surface exists because the headers exist.

That has clear consequences for how the library behaves over time. New LLVM APIs appear in the headers, and a regeneration picks them up. APIs that disappear or change signature in the headers can leave you with a managed surface that no longer matches the native library you happen to have installed. A generated binding is a mirror of a specific header set, and the mirror is only as good as the regeneration step that produced it.

The build scripts in the repository make the same point from the native side. The native runtime packages are produced by CI, in the workflow file.github/workflows/regenerate-native.yml, rather than by hand. Two artifacts come out of that: libLLVM.runtime packages, plus the libLLVM meta-package, which carry the prebuilt shared libLLVM library, and libLLVMSharp.runtime packages, plus the libLLVMSharp meta-package, which carry the libLLVMSharp helper compiled from sources/libLLVMSharp against the matching LLVM release. The word matching is the important one, and the project enforces it in CI.

## Nearly the C API, with one deliberate difference

The managed surface is described in the README as nearly identical to the LLVM C APIs, with a worked example. In C the call is LLVMModuleCreateWithName, and in C# it is LLVM.ModuleCreateWithName, and the README points out the dot in the C# name. That is a small thing, and it is exactly the kind of small thing that decides whether a binding feels learnable.

The other stated feature is type safety, with a specific example: LLVMValueRef and LLVMTypeRef are different types even though both are pointers internally. The C API expresses that distinction through typedefs over opaque pointer types, and a binding that collapsed them into one pointer type would lose information. Keeping them apart means a C# caller cannot pass a value reference where a type reference belongs without a cast, which is a real error class in LLVM interop caught at compile time rather than at an assertion inside native code.

There is a limit worth naming. The mapping is mechanical, so it inherits the shape of C. C has no namespaces in the C# sense beyond the prefix, so the C# API uses a dot to separate the LLVM prefix from the function name, and the result is a flat, wide surface. The readability of the C# is a property of the C, and the README does not claim otherwise. For a library with this many entry points, that is a reasonable trade, but it is a trade, and a reader coming from a hand written wrapper library should expect the difference rather than discover it later.

## Building from source takes a clone and a dotnet build

The README gives build instructions for two platforms, and both are the same three commands with a different working directory. On Linux using .NET Core the sequence is a clone, a change into the LLVMSharp directory, and a build. On Windows the same steps run from the Visual Studio Developer Command Prompt, and the README flags that requirement explicitly.

```bash
$ git clone http://github.com/dotnet/llvmsharp
$ cd LLVMSharp
$ dotnet build
```

The Windows variant is identical apart from the capitalisation of the repository path in the clone command, and the note about the Developer Command Prompt is the only platform difference stated.

What you get from a source build is the managed side. The native library is not produced by that command. Native runtime packages are built by the CI workflow and published to NuGet, so consuming LLVMSharp in an application means taking a managed package and a native package together, and the versions of the two have to line up. The repository layout reflects that division, with a packages directory holding the package definitions, a scripts directory holding the build helpers, a sources directory holding the library and the native helper, a samples directory that includes a Kaleidoscope tutorial, and separate tests and scripts directories with their own test and pack entry points. There is also a top-level CMakeLists.txt, a global.json, Directory.Packages.props and Directory.Build.props and targets files, which is the standard layout for central package version management across several projects in one repository.

## The Windows shared library gap and how the resolver papers over it

The most interesting piece of engineering in the repository is a gap in the upstream LLVM releases. The README explains that the official LLVM releases only ship a shared libLLVM on Windows, where it is shipped under the official name LLVM-C.dll. Everything else comes from other places: Linux and macOS are lifted from the most official prebuilt source per platform, which is the LLVM project's own Debian and Ubuntu repository apt.llvm.org, providing the libllvm<major> package, on Linux, and the Homebrew llvm formula on macOS. Both of those link the standard system C++ runtime. Windows and Linux compile libLLVMSharp against the official release SDK, while macOS uses the exact Homebrew formula on a macOS 15 runner and requires users to install that formula for its external dependencies.

ClangSharp does not have this problem in the same form, because it lifts the shared libclang that ships for every platform. That contrast is worth internalising, because it explains why the native packaging for LLVMSharp is more involved than the native packaging for its generator.

The managed side handles the naming difference. The resolver in sources/LLVMSharp.Interop/LLVM.cs maps the logical libLLVM P/Invoke name to the per-platform file, which is LLVM-C.dll on Windows and libLLVM.so or libLLVM.dylib elsewhere. The README also notes that this is why the static LLVM-C.dll import inside libLLVMSharp.dll resolves without an extra copy, so the Windows case is handled inside the helper rather than by asking every consumer to rename files.

## Three files have to agree on the LLVM version, and CI checks it

Version tracking is where native interop projects usually break, and this one makes the check explicit. The tracked LLVM version is the project(LLVMSharp VERSION X.Y.Z) value in the top-level CMakeLists.txt, and that value maps to the llvmorg-X.Y.Z release tag.

The native packages are then built per runtime identifier, the README listing win-x64, win-arm64, linux-x64, linux-arm64 and osx-arm64, with the download and stage step handled by scripts/build.ps1 on Windows and scripts/build.sh elsewhere. The staged binary is written to artifacts/native/<rid>/.

```
./scripts/build.sh --regeneratenative --target libLLVM --rid linux-x64
./scripts/build.sh --regeneratenative --target libLLVMSharp --rid linux-x64
```

The trigger rules are also stated. The libLLVM job runs when the tracked LLVM major and minor version changes, or when the workflow is dispatched manually with the libllvm input set. The libLLVMSharp job runs on the same version change, or when anything under sources/libLLVMSharp changes, or on a manual dispatch with the libllvmsharp input set.

Before anything is downloaded, the workflow verifies that the nuspec and runtime.json versions under the packages directory match the tracked LLVM version, with libLLVM matching exactly and libLLVMSharp following the pattern <llvm-version>.<revision>. A version bump that forgot to update a package fails fast, and the README says the same rule applies to a human making the change: bump the version in CMakeLists.txt alongside the version in the affected nuspec files, the versions in runtime.json and the README, and pushing that change regenerates the packages. Each job uploads the resulting .nupkg files as build artifacts and signs them, and publishing to NuGet.org is a manual step, so there is a deliberate human checkpoint between building a package and releasing it.

## What the C API leaks through, and when this is the wrong tool

Because the mapping is mechanical, everything about using LLVM from C is still true here. Pointers stay pointers, ownership rules stay manual, and an LLVMContext must be disposed explicitly. The generated library does not add a disposable scope, does not translate C lifetime rules into a garbage collected model, and does not offer a safe subset. If your C# code frees something twice, it crashes in the same way it would in C.

That is the wrong tool if you only need to parse or generate code for a narrow purpose. A.NET developer who wants to emit a little IR by hand may find the whole LLVM C surface excessive, and the licensing of the generated code does not change that: LLVMSharp is MIT licensed, and it says so plainly, with the copyright held by the.NET Foundation and Contributors, and the terms in LICENSE.md in the repository root. That permissive licence applies to the bindings themselves. The licence of the LLVM libraries you load through them is a separate question, and the README does not restate it, so read the LICENSE.md file and the notices in the repository, including NOTICE.md, before redistributing a binary that bundles native code.

On maintenance, the record is healthy but the version spread is something to plan around. The last push to the repository was on 2026-09-02, and the repository is not archived. The published releases include v22.1.8 on 2026-09-01, v21.1.8.1 on 2026-08-31 and v20.1.2 on 2025-05-24. Two releases in consecutive days and then a gap of more than a year to the previous one is a pattern worth noting: consumers who need a specific LLVM release should expect to pin to a specific tag and rebuild, because the library tracks upstream LLVM rather than trying to support several at once.

## Conclusion

Adopt LLVMSharp if you are writing C# and need LLVM's code generation, and you accept a surface that mirrors the C headers one to one, plus a native library you have to ship and version alongside your managed code. Do not adopt it if you want a small, pleasant .NET API, if you cannot manage native dependencies, or if you need a hand tuned binding surface, because nothing here smooths over the C API. Verify first that the libLLVM native package for your runtime identifier matches the LLVM version tracked in CMakeLists.txt, since a mismatch there is the failure this project is built to avoid, and read the Windows note about LLVM-C.dll before you ship anything to a Windows user.

## FAQ

### What is LLVMSharp used for?

LLVMSharp is a .NET Standard library that exposes the LLVM infrastructure to C# through bindings auto-generated from the LLVM C headers with ClangSharp. The README says the bindings cover all functionality exposed by those headers, which is more than enough to build a full compiler.

### How do I build LLVMSharp from source?

Clone the repository, change into the LLVMSharp directory and run dotnet build. On Windows the README says to run the same commands from the Visual Studio Developer Command Prompt.

### Which NuGet packages do I need to use LLVMSharp?

The managed library is published as the llvmsharp package, and a separate convenience package named libLLVM provides the native libLLVM library for several platforms. The native runtime packages libLLVM.runtime and libLLVMSharp.runtime are produced by CI rather than by hand.

### How does LLVMSharp keep the native library and the managed code in sync?

The tracked LLVM version lives in the project(LLVMSharp VERSION X.Y.Z) value in CMakeLists.txt, and before anything is downloaded the workflow checks that the nuspec and runtime.json versions under the packages directory match it, so a bump that forgot to update a package fails fast. Publishing the resulting signed packages to NuGet.org remains a manual step.

### What licence does LLVMSharp use?

The bindings are MIT licensed, with copyright held by the .NET Foundation and Contributors, and the terms are in LICENSE.md in the repository root. The licence of the underlying LLVM libraries is a separate matter, and the README does not restate it.

## Sources

- [dotnet/LLVMSharp on GitHub](https://github.com/dotnet/LLVMSharp)
- [Issues](https://github.com/dotnet/LLVMSharp/issues)
- [License: MIT](https://github.com/dotnet/LLVMSharp/blob/main/LICENSE)
- [README](https://github.com/dotnet/LLVMSharp/blob/main/README.md)
- [Releases](https://github.com/dotnet/LLVMSharp/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/dotnet-llvmsharp
