# How to Enable Z3 Solver Support in LLVM for Symbolic Execution

> Learn how to enable Z3 solver support in LLVM for symbolic execution. Configure your build with LLVM_ENABLE_Z3_SOLVER=ON and streamline your static analysis workflow.

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

---

**Enable Z3 solver support in LLVM by configuring your build with `-DLLVM_ENABLE_Z3_SOLVER=ON` and optionally specifying `-DLLVM_Z3_INSTALL_DIR` to locate the Z3 headers and libraries, which defines the `LLVM_WITH_Z3` macro and exposes the `CreateZ3Solver()` factory for runtime use.**

LLVM's symbolic execution engine leverages SMT solvers to analyze program paths and verify constraints. The llvm/llvm-project repository includes a native Z3 theorem prover integration implemented in [`llvm/lib/Support/Z3Solver.cpp`](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/Z3Solver.cpp), but this functionality is disabled by default and requires explicit build-time configuration to enable Z3 solver support in LLVM.

## Prerequisites for Z3 Integration

Before configuring LLVM, you must install the Z3 development libraries on your system. The build requires the Z3 header file [`z3.h`](https://github.com/llvm/llvm-project/blob/main/z3.h) and the library file (`libz3.so` on Linux or `libz3.a` for static linking) to be available in standard or user-specified paths.

If Z3 is installed in a non-standard location, you will need the root path to pass to CMake during the LLVM configuration phase.

## CMake Configuration Flags

Two CMake options control how LLVM discovers and enables Z3 support:

**`LLVM_ENABLE_Z3_SOLVER`** – Set this boolean option to `ON` to enable the Z3 backend. According to the source code in [`llvm/lib/Support/Z3Solver.cpp`](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/Z3Solver.cpp), this defines the `LLVM_WITH_Z3` preprocessor macro, which gates the compilation of all Z3-specific solver code behind `#if LLVM_WITH_Z3` blocks.

**`LLVM_Z3_INSTALL_DIR`** – Specify the root directory of your Z3 installation when it resides outside standard system paths. The module `llvm/cmake/modules/FindZ3.cmake` checks this variable first, then searches system paths for [`z3.h`](https://github.com/llvm/llvm-project/blob/main/z3.h) and the Z3 libraries.

### Example Configuration Command

Configure LLVM with Z3 support using the following CMake invocation:

```bash
cmake -G Ninja \
  -DLLVM_ENABLE_PROJECTS="llvm;clang" \
  -DLLVM_ENABLE_Z3_SOLVER=ON \
  -DLLVM_Z3_INSTALL_DIR=/opt/z3 \
  /path/to/llvm-project/llvm

```

This command enables the solver, points to a custom Z3 installation at `/opt/z3`, and prepares the build for the Ninja generator.

## Building LLVM with Z3 Support

After running CMake, execute your build command (`ninja` or `make`). The Z3 solver implementation compiles only when `LLVM_WITH_Z3` is defined. If the Z3 headers or libraries are missing or incorrectly specified, the build will fail with linker errors or "file not found" messages for [`z3.h`](https://github.com/llvm/llvm-project/blob/main/z3.h).

A successful build links against the Z3 libraries and includes the factory function for creating solver instances.

## Runtime Usage of the Z3 Solver

Once LLVM is compiled with Z3 support, obtain a Z3-backed solver instance through the factory function defined at the end of [`llvm/lib/Support/Z3Solver.cpp`](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/Z3Solver.cpp):

```cpp
llvm::SMTSolverRef Solver = llvm::CreateZ3Solver();

```

If LLVM was built without Z3 support, calling this function triggers a fatal error that terminates the program with a message instructing you to rebuild with `-DLLVM_ENABLE_Z3_SOLVER=ON`.

### Working with Bitvector Constraints

The following example demonstrates using LLVM's SMT API to create bitvector variables and check satisfiability:

```cpp
#include "llvm/Support/SMTAPI.h"

int main() {
  // Obtain a Z3 solver (requires LLVM built with Z3 support)
  llvm::SMTSolverRef Solver = llvm::CreateZ3Solver();

  // Create 8-bit bitvector sort
  auto BV8 = Solver->getBitvectorSort(8);
  
  // Create symbolic variables
  auto X = Solver->mkSymbol("x", BV8);
  auto Y = Solver->mkSymbol("y", BV8);

  // Build constraint: x + y == 42
  auto Sum = Solver->mkBVAdd(X, Y);
  auto C42 = Solver->mkBitvector(llvm::APSInt(42), 8);
  Solver->addConstraint(Solver->mkEqual(Sum, C42));

  // Check satisfiability
  auto Result = Solver->check();
  if (Result && *Result)
    llvm::outs() << "SAT\n";
  else
    llvm::outs() << "UNSAT or UNKNOWN\n";

  return 0;
}

```

The solver supports bitvector arithmetic, boolean logic, and floating-point operations as implemented in the Z3 backend.

## Summary

- **Enable Z3 solver support in LLVM** by setting `-DLLVM_ENABLE_Z3_SOLVER=ON` during CMake configuration to define the `LLVM_WITH_Z3` macro.
- Use **`LLVM_Z3_INSTALL_DIR`** to specify custom Z3 installation paths when not using system defaults.
- The integration lives in **[`llvm/lib/Support/Z3Solver.cpp`](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/Z3Solver.cpp)**, guarded by conditional compilation directives.
- Call **`llvm::CreateZ3Solver()`** at runtime to obtain a Z3-backed solver instance.
- The API supports **bitvectors**, floating-point constraints, and boolean operations for symbolic execution tasks.

## Frequently Asked Questions

### What happens if I call CreateZ3Solver() without enabling Z3 during the build?

The function implementation includes a fallback branch that terminates the program with a fatal error, displaying a message that reminds you to rebuild LLVM with `-DLLVM_ENABLE_Z3_SOLVER=ON`. This prevents symbolic execution tools from silently failing when solver support is expected but not compiled.

### Does LLVM support alternative SMT solvers besides Z3?

While LLVM's **[`llvm/lib/Support/SMTAPI.h`](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/SMTAPI.h)** defines a generic solver interface, the upstream llvm/llvm-project repository currently maintains only the Z3 backend in [`llvm/lib/Support/Z3Solver.cpp`](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/Z3Solver.cpp). Supporting additional solvers would require implementing the SMT API for those specific backends.

### Can I use a system-installed Z3 without specifying LLVM_Z3_INSTALL_DIR?

Yes. The `FindZ3.cmake` module searches standard system paths (such as `/usr/lib` and `/usr/include`) automatically. You only need to set **`LLVM_Z3_INSTALL_DIR`** when Z3 is installed in a custom prefix or when you need to override the system version with a specific build.

### Are floating-point operations supported through the Z3 backend?

Yes. The implementation in [`llvm/lib/Support/Z3Solver.cpp`](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/Z3Solver.cpp) supports floating-point sorts and arithmetic operations alongside bitvectors and booleans, enabling comprehensive symbolic execution analysis of numeric computations.