Troubleshooting¶
Look at build/<preset>/pebble-env.txt first. It records exactly which compiler, LLVM, FileCheck, lit, and GoogleTest the build found. ./course doctor checks the same file.
"My pass does nothing" — optnone¶
At -O0, clang marks every function optnone. The new pass manager then skips your pass on those functions without saying anything. To generate test IR from C, use:
In lit tests, the %clang-ir substitution does exactly this and writes to stdout. To check an existing .ll file, run grep optnone foo.ll. If it's there, delete optnone from the attributes #N = { ... } lines, or regenerate the file. Also run opt -passes=mem2reg first if your pass expects SSA values rather than allocas.
Plugin loading¶
opt -load-pass-plugin=build/<preset>/lib/PebblePasses.so can fail in several ways. The plugin ends in .so on macOS too, because that is how CMake names MODULE libraries there.
| Error | Cause and fix |
|---|---|
Could not load library ... image not found / cannot open shared object file |
The path is wrong or the plugin isn't built yet. Run cmake --build --preset <p>. In lit, %plugin pointing at NOT-BUILT-... means the target doesn't exist yet. |
Symbol not found: __ZN4llvm... (macOS) or undefined symbol: _ZN4llvm... (Linux) |
The plugin was loaded into a different LLVM than it was compiled against. Use the opt from the same LLVM 23 install. %opt does this, and so does $(brew --prefix llvm)/bin/opt. Never use an opt from another version on PATH. |
unknown pass name 'pebble-xyz' |
The plugin loaded, but its registerPipelineParsingCallback doesn't match that name. Check the spelling, and check that llvmGetPassPluginInfo is extern "C". |
Plugin API version mismatch / wrong API version |
The plugin was built against headers from another LLVM version. Delete the build directory and reconfigure. |
| Crash at load time on macOS | Usually a toolchain mix: the plugin was built with Apple clang and loaded into Homebrew opt. The macos preset prevents this, so reconfigure from scratch with cmake --preset macos. |
CommandLine Error: Option 'xyz' registered more than once |
LLVM ended up in the process twice: the plugin statically links LLVM libraries and opt also contains them. Plugins made with pebble_add_pass_plugin don't link LLVM themselves. Don't add LLVM libraries to a plugin's DEPS. |
On macOS, plugins link with -undefined dynamic_lookup, so LLVM symbols are resolved from the loading opt at runtime. On Linux, shared objects allow undefined symbols by default.
rpath: libLLVM.so.23.1: cannot open shared object file / Library not loaded: @rpath/libLLVM.dylib¶
The course binaries are built with LLVM_LIBRARY_DIR on their rpath (CMAKE_BUILD_RPATH), so they find libLLVM without any environment variables. If you see this error:
- You moved or copied the binary out of the build tree, or LLVM was upgraded underneath it. Rebuild with
cmake --build --preset <p>, or reconfigure from scratch if LLVM moved. - Setting
LD_LIBRARY_PATH(Linux) as a stopgap works. On macOS,DYLD_LIBRARY_PATHis stripped by SIP for system binaries, so prefer rebuilding. otool -l <binary> | grep -A2 LC_RPATH(macOS) orreadelf -d <binary> | grep RUNPATH(Linux) shows the rpath.
RTTI mismatch: undefined reference to typeinfo for llvm::...¶
LLVM may be built with or without RTTI (LLVM_ENABLE_RTTI), and your code has to match. The build reads LLVM's setting and adds -fno-rtti when needed through the pebble_settings target. If you see this error:
- You created a target with plain
add_library/add_executableinstead of thepebble_add_*helpers. Link it topebble_settings, or use a helper. - Don't use
dynamic_castortypeidon LLVM classes. Use LLVM'sisa<>/cast<>/dyn_cast<>.
Exceptions work the same way. LLVM is built with -fno-exceptions, and course code must report errors with std::expected or llvm::Expected, never throw. Unit-test executables keep exceptions enabled because GoogleTest uses them.
FileCheck not found¶
The lit tests need FileCheck, and not every LLVM package installs it in the same place:
| Install | FileCheck location |
|---|---|
| Homebrew | $(brew --prefix llvm)/bin/FileCheck |
| apt.llvm.org | /usr/lib/llvm-23/bin/FileCheck (package llvm-23-tools; llvm.sh 23 all installs it) |
| conda-forge | <prefix>/libexec/llvm/FileCheck |
CMake searches all of these, and also PATH for FileCheck and FileCheck-23. To point it at a specific copy:
not is optional. If LLVM's not isn't found, %not falls back to lit's built-in not.
Other problems¶
- "Apple clang ... detected": this is intentional. See macos.md. Delete
build/<preset>and runcmake --preset macos. - Undefined libc++ symbols on macOS (for example
std::__1::__is_posix_terminal): the headers of Homebrew's libc++ are newer than the system libc++ that gets linked. Reconfigure with-DPEBBLE_USE_HOMEBREW_LIBCXX=ON. That links Homebrew's libc++ and puts it on the rpath. The imported target "..." references the file ".../libPolly.a" but this file does not exist(apt): installlibpolly-23-dev, pluslibzstd-dev libxml2-dev libedit-dev libcurl4-openssl-devif CMake names those.find_package(LLVM)picks up LLVM 18 (or another version): the error message tells you which one it found. Pass-DLLVM_DIR=<LLVM 23 prefix>/lib/cmake/llvm.- lit/PyYAML version warnings: run
uv sync(orcmake --build build/<preset> --target setup-venv) and then reconfigure. course: uv was not found: install uv (macOSbrew install uv; Linuxcurl -LsSf https://astral.sh/uv/install.sh | sh), thenuv sync. If you manage Python yourself,COURSE_NO_UV=1 ./course …skips uv (PyYAML and lit must be importable)../course servesays mkdocs-material is missing:uv sync(thesitegroup is installed by default;uv sync --no-group siteleaves it out).- Changing
PEBBLE_USE_SOLUTIONdoes nothing: it's a cache variable, so pass it again with-D...and then rebuild. The configure output lists every component taken fromsolutions/. - A test prints
TODO(chNN): ...and exits with code 99: that's not a bug. It's an unimplemented exercise. Implement it, or use-DPEBBLE_USE_SOLUTION=<switch>.
conflicting types for '__cxa_init_primary_exception' (Linux)¶
You installed libc++abi-23-dev, probably through llvm.sh 23 all. Its cxxabi.h lives in /usr/lib/llvm-23/include, which is on the include path for LLVM's headers, so it shadows libstdc++'s version. Fix: sudo apt remove libc++abi-23-dev libc++-23-dev. The course builds against libstdc++ on Linux, the same library Ubuntu's LLVM packages use.