How to Enable Z3 Solver Support in LLVM for Symbolic Execution

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, 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 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, 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 and the Z3 libraries.

Example Configuration Command

Configure LLVM with Z3 support using the following CMake invocation:

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.

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:

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:

#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, 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 defines a generic solver interface, the upstream llvm/llvm-project repository currently maintains only the Z3 backend in 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 supports floating-point sorts and arithmetic operations alongside bitvectors and booleans, enabling comprehensive symbolic execution analysis of numeric computations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →