Scaffold an out-of-tree LLVM 22 compiler/language project from scratch. Covers CMake configuration, finding LLVM, linking components, a minimal LLVMContext driver, and build instructions.
64
76%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./tiles/tessl-llvm/skills/out-of-tree-setup/SKILL.mdUse this skill when the user wants to start a new compiler, language runtime, or LLVM-based tool that builds against an installed LLVM 22 rather than inside the LLVM source tree.
Confirm the user has LLVM 22 installed. Common locations:
# Homebrew (macOS)
brew install llvm@22
# Path: /opt/homebrew/opt/llvm@22
# apt (Ubuntu/Debian)
apt install llvm-22-dev libclang-22-dev
# Path: /usr/lib/llvm-22
# From source
cmake --install build --prefix /usr/local/llvm-22Find the cmake config dir:
llvm-config-22 --cmakedir
# or
find /usr -name "LLVMConfig.cmake" 2>/dev/nullCreate this minimal layout:
<project>/
├── CMakeLists.txt
├── include/
│ └── <Project>.h
└── src/
└── main.cppScale up later: add lib/, test/, tools/ as needed.
CMakeLists.txtcmake_minimum_required(VERSION 3.20)
project(<ProjectName> CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# ── Find LLVM 22 ──────────────────────────────────────────────────────────────
find_package(LLVM 22 REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
# ── Map component names to library names ──────────────────────────────────────
# Common components — add/remove as needed:
# Core — LLVMContext, Module, IRBuilder, basic IR types
# Support — raw_ostream, StringRef, Error, CommandLine
# Analysis — pass analyses (DominatorTree, ScalarEvolution, etc.)
# Passes — New Pass Manager, PassBuilder, StandardInstrumentations
# IRReader — parseIRFile / parseAssemblyString
# BitWriter — WriteBitcodeToFile
# Target — TargetMachine, TargetRegistry
# X86 — X86 target (or AArch64, RISCV, etc.)
llvm_map_components_to_libnames(LLVM_LIBS
Core
Support
Analysis
Passes
IRReader
BitWriter
Target
X86CodeGen # replace/extend with your target
X86AsmParser
X86Desc
X86Info
)
# ── Build the executable ──────────────────────────────────────────────────────
add_executable(<ProjectName> src/main.cpp)
target_include_directories(<ProjectName> PRIVATE
${CMAKE_SOURCE_DIR}/include
)
target_link_libraries(<ProjectName> PRIVATE ${LLVM_LIBS})Note: Use
llvm_map_components_to_libnames— do NOT hardcode-lLLVMCoreetc. Component names are case-sensitive and listed inllvm-config --components.
src/main.cppThis compiles, creates a module, prints IR, and JIT-compiles a trivial function:
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
int main(int argc, char **argv) {
// InitLLVM handles signal handlers, pretty-stack-trace, and ManagedStatic.
InitLLVM X(argc, argv);
// Initialize targets (required before any codegen or JIT).
InitializeAllTargetInfos();
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmParsers();
InitializeAllAsmPrinters();
LLVMContext Ctx;
auto M = std::make_unique<Module>("hello", Ctx);
M->setTargetTriple(sys::getDefaultTargetTriple());
// Build: define i32 @add(i32 %a, i32 %b) { ret i32 add(a, b) }
IRBuilder<> Builder(Ctx);
FunctionType *FT = FunctionType::get(
Builder.getInt32Ty(),
{Builder.getInt32Ty(), Builder.getInt32Ty()},
/*isVarArg=*/false);
Function *F = Function::Create(FT, Function::ExternalLinkage, "add", *M);
F->getArg(0)->setName("a");
F->getArg(1)->setName("b");
BasicBlock *Entry = BasicBlock::Create(Ctx, "entry", F);
Builder.SetInsertPoint(Entry);
Value *Sum = Builder.CreateAdd(F->getArg(0), F->getArg(1), "sum");
Builder.CreateRet(Sum);
// Verify the module.
if (verifyModule(*M, &errs())) {
errs() << "Module verification failed\n";
return 1;
}
// Print IR to stdout.
M->print(outs(), nullptr);
return 0;
}# Configure — point CMAKE_PREFIX_PATH at your LLVM 22 install prefix
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/path/to/llvm-22/install
# or: -DLLVM_DIR=$(llvm-config-22 --cmakedir)
# Build
cmake --build build -j$(nproc)
# Run
./build/<ProjectName>Expected output: LLVM IR for the add function.
#include "llvm/Passes/PassBuilder.h"
// After module is built and verified:
PassBuilder PB;
LoopAnalysisManager LAM;
FunctionAnalysisManager FAM;
CGSCCAnalysisManager CGAM;
ModuleAnalysisManager MAM;
// Register all the standard analyses.
PB.registerModuleAnalyses(MAM);
PB.registerCGSCCAnalyses(CGAM);
PB.registerFunctionAnalyses(FAM);
PB.registerLoopAnalyses(LAM);
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
// Build a standard O2 pipeline.
ModulePassManager MPM = PB.buildPerModuleDefaultPipeline(OptimizationLevel::O2);
MPM.run(*M, MAM);If you want to ship a pass loadable by opt --load-pass-plugin, see the
add-npm-pass skill. Add this to your CMakeLists.txt alongside the executable:
add_library(MyPassPlugin MODULE src/MyPass.cpp)
target_include_directories(MyPassPlugin PRIVATE ${LLVM_INCLUDE_DIRS} include)
target_compile_definitions(MyPassPlugin PRIVATE ${LLVM_DEFINITIONS})
set_target_properties(MyPassPlugin PROPERTIES
CXX_STANDARD 17
POSITION_INDEPENDENT_CODE ON
)llvm-config --libs output directly in CMake — use llvm_map_components_to_libnames instead; the cmake integration handles RPATH and ordering correctly.InitializeAll*() functions after constructing a TargetMachine — initialize before.LLVMConfig.cmake.new Module(...) — use std::make_unique<Module>(...).Value * pointers after IR modifications that can RAUW (Replace All Uses With) them.verifyModule during development; it catches IR invariant violations early.a251bde
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.