Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/llvm-trunk.env
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Pinned upstream LLVM trunk commit that Morok is built against.
#
# Morok targets the New-PM plugin API at <llvm/Plugins/PassPlugin.h> with
# LLVM_PLUGIN_API_VERSION 2 (introduced upstream by llvm/llvm-project#173279,
# commit f54df0d09e19, 2025-12-22). Any trunk commit at or after that works; this
# is plain upstream trunk, not a fork.
# LLVM_PLUGIN_API_VERSION 2 or 3. The plugin uses the API version declared by
# its build headers; clang/opt must use that same LLVM installation. Morok also
# uses trunk-only IR/pass APIs, so each updated pin must pass the full suite.
# This is plain upstream trunk, not a fork.
#
# We build LLVM from source (the LLVM.org prebuilt release toolchains cannot be
# linked against on macOS — their libc++ has diverged from Apple's — and Morok
# also uses trunk-only LLVM APIs). The build is expensive once per pinned commit
# and then cached by SHA, so day-to-day runs are minutes.
#
# Bumped by .github/workflows/llvm-bump.yml as trunk advances.
LLVM_REF=cf08d4bc700245f85ce528f6c9f4d9a3fdd316a0
LLVM_REF=201b446bf71c7c6d37a9bb195ed5e448c5231070
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ jobs:
args=(
-S . -B build -G Ninja
-DCMAKE_BUILD_TYPE=RelWithDebInfo
-DMOROK_BUILD_PLUGIN=ON
-DMOROK_BUILD_TESTS=ON
-DCMAKE_C_COMPILER="${{ steps.llvm.outputs.clang }}"
-DCMAKE_CXX_COMPILER="${{ steps.llvm.outputs.clangxx }}"
-DLLVM_DIR="${{ steps.llvm.outputs.llvm-dir }}"
Expand Down Expand Up @@ -181,7 +183,9 @@ jobs:
env:
CMAKE_C_COMPILER_LAUNCHER: sccache
CMAKE_CXX_COMPILER_LAUNCHER: sccache
run: cmake --build build --parallel
run: |
cmake --build build --target morok_plugin morok_ir_tests --parallel
cmake --build build --parallel

# Explicit save after all compilation so the object cache is uploaded even
# if the tests below fail.
Expand Down Expand Up @@ -209,7 +213,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
ctest --test-dir build -L ir --output-on-failure --timeout 300
ctest --test-dir build -L ir --no-tests=error --output-on-failure --timeout 300

- name: Run tests
shell: bash
Expand Down
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ if(MOROK_BUILD_PLUGIN OR MOROK_BUILD_TESTS)
morok_add_layer(src/passes)
morok_add_layer(src/pipeline)
else()
if(MOROK_BUILD_PLUGIN)
message(FATAL_ERROR
"MOROK_BUILD_PLUGIN=ON requires a usable LLVM trunk installation.\n"
"Point LLVM_DIR at the LLVM build used by clang/opt, or explicitly set\n"
"MOROK_BUILD_PLUGIN=OFF to build only the available layers.")
endif()
message(WARNING
"LLVM not usable for plugin/IR layers — building pure layers only.")
endif()
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ morok-native-pack Host executable used by the Linux native-pack build stage.
- Ninja.
- A C11 and C++23 capable toolchain.
- LLVM 18 or newer with the New-PM plugin API Morok targets. The current CI and
development toolchains use the API-v2 plugin header at
development toolchains use the API-v2/v3 plugin header at
`<llvm/Plugins/PassPlugin.h>`.

Morok requires the LLVM headers and the `clang`/`opt` binaries used at runtime
to agree on the same New-PM pass plugin ABI. The build currently checks for
`<llvm/Plugins/PassPlugin.h>` with `LLVM_PLUGIN_API_VERSION == 2`; older LLVM
`<llvm/Plugins/PassPlugin.h>` with `LLVM_PLUGIN_API_VERSION` 2 or 3; older LLVM
installs that expose only `<llvm/Passes/PassPlugin.h>` with API version 1 are a
different plugin ABI and are rejected by
[`cmake/MorokLLVM.cmake`](cmake/MorokLLVM.cmake) instead of failing later with a
Expand All @@ -134,7 +134,7 @@ Useful CMake options:

```text
MOROK_BUILD_TESTS=ON build tests
MOROK_BUILD_PLUGIN=ON build libMorok
MOROK_BUILD_PLUGIN=ON build libMorok; fail configuration if LLVM is unusable
MOROK_WERROR=OFF treat warnings as errors when ON
MOROK_SANITIZE=OFF ASan/UBSan for pure layers/tests when ON
```
Expand Down Expand Up @@ -1230,7 +1230,7 @@ self-check data region.

| Symptom | Likely cause | Fix |
|---|---|---|
| CMake cannot find `llvm/Plugins/PassPlugin.h` | Host LLVM is missing the API-v2 New-PM plugin header | Point `LLVM_DIR` at the same API-v2 LLVM install used by `clang`/`opt`. |
| CMake cannot find `llvm/Plugins/PassPlugin.h` | Host LLVM is missing the API-v2/v3 New-PM plugin header | Point `LLVM_DIR` at the same API-v2/v3 LLVM install used by `clang`/`opt`. |
| Plugin load reports API/version mismatch | `clang`/`opt` and Morok were built against different LLVM plugin ABIs | Rebuild Morok with the same LLVM used by the host driver. |
| `-mllvm -morok` is unknown on Windows | Windows plugin cl::opts are not parsed by host clang the same way | Use `MOROK_ENABLE=1` plus `MOROK_CONFIG`, `MOROK_PRESET`, and `MOROK_SEED`. |
| Static Linux binary crashes around import indirection | FCO was left enabled in a static link | Use `cross_build.sh` or force `[passes.function_call_obfuscate].enabled = false` and `[passes.platform_runtime].static_link_expected = true`. |
Expand Down
19 changes: 10 additions & 9 deletions cmake/MorokLLVM.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
# MorokLLVM.cmake
#
# Locates LLVM and validates that it exposes the New-PM plugin API this project
# targets. This environment ships a *customised* LLVM where the plugin header
# was moved to <llvm/Plugins/PassPlugin.h> and the API version is 2 (upstream
# uses <llvm/Passes/PassPlugin.h>, version 1). We fail loudly if the header /
# version we depend on is not present, rather than producing a plugin that the
# host `opt`/`clang` will reject at load time with a cryptic version error.
# targets. Upstream trunk exposes <llvm/Plugins/PassPlugin.h> with API version
# 2 or 3. The plugin advertises the version from the headers it compiles against;
# its host `opt`/`clang` must come from the same LLVM build.
#
# Produces:
# Morok_LLVM_FOUND BOOL — set when everything validated
Expand Down Expand Up @@ -35,24 +33,27 @@ if(LLVM_VERSION_MAJOR VERSION_LESS 18)
endif()

# --- Validate the New-PM plugin API we compile against ----------------------
# Do not reuse a header from a previously configured LLVM_DIR.
unset(MOROK_PASSPLUGIN_HEADER CACHE)
find_file(MOROK_PASSPLUGIN_HEADER
NAMES llvm/Plugins/PassPlugin.h
PATHS ${LLVM_INCLUDE_DIRS}
NO_DEFAULT_PATH)
NO_DEFAULT_PATH
NO_CACHE)

if(NOT MOROK_PASSPLUGIN_HEADER)
message(WARNING
"MorokLLVM: <llvm/Plugins/PassPlugin.h> not found under ${LLVM_INCLUDE_DIRS}.\n"
"This build targets a forked LLVM that moved the plugin header there.")
"This build requires the upstream trunk plugin header.")
return()
endif()

file(STRINGS "${MOROK_PASSPLUGIN_HEADER}" _api_line
REGEX "define[ \t]+LLVM_PLUGIN_API_VERSION")
string(REGEX MATCH "[0-9]+" MOROK_PLUGIN_API_VERSION "${_api_line}")
if(NOT MOROK_PLUGIN_API_VERSION STREQUAL "2")
if(NOT MOROK_PLUGIN_API_VERSION MATCHES "^(2|3)$")
message(WARNING
"MorokLLVM: expected LLVM_PLUGIN_API_VERSION 2, got '${MOROK_PLUGIN_API_VERSION}'.")
"MorokLLVM: expected LLVM_PLUGIN_API_VERSION 2 or 3, got '${MOROK_PLUGIN_API_VERSION}'.")
return()
endif()

Expand Down
3 changes: 1 addition & 2 deletions include/morok/passes/AdversarialFunctionMerging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,14 @@ bool adversarialFunctionMergingModule(llvm::Module &M,

/// New-PM module-pass wrapper for standalone use (`-passes=morok-afm`).
class AdversarialFunctionMergingPass
: public llvm::PassInfoMixin<AdversarialFunctionMergingPass> {
: public llvm::RequiredPassInfoMixin<AdversarialFunctionMergingPass> {
public:
explicit AdversarialFunctionMergingPass(AdversarialMergeParams params = {},
std::uint64_t seed = 0x1337)
: params_(params), engine_(core::Xoshiro256pp::fromSeed(seed)) {}

llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &AM);
static bool isRequired() { return true; }

private:
AdversarialMergeParams params_;
Expand Down
3 changes: 1 addition & 2 deletions include/morok/passes/AdversarialSelfTuning.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,14 @@ bool adversarialSelfTuneModule(llvm::Module &M,

/// New-PM module-pass wrapper for standalone use (`-passes=morok-selftune`).
class AdversarialSelfTuningPass
: public llvm::PassInfoMixin<AdversarialSelfTuningPass> {
: public llvm::RequiredPassInfoMixin<AdversarialSelfTuningPass> {
public:
explicit AdversarialSelfTuningPass(AdversarialTuningParams params = {},
std::uint64_t seed = 0x1337)
: params_(params), engine_(core::Xoshiro256pp::fromSeed(seed)) {}

llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &AM);
static bool isRequired() { return true; }

private:
AdversarialTuningParams params_;
Expand Down
3 changes: 1 addition & 2 deletions include/morok/passes/AliasOpaquePredicates.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,14 @@ bool aliasOpaquePredicatesFunction(llvm::Function &F,

/// New-PM wrapper for standalone use (`-passes=morok-aliasop`).
class AliasOpaquePredicatesPass
: public llvm::PassInfoMixin<AliasOpaquePredicatesPass> {
: public llvm::RequiredPassInfoMixin<AliasOpaquePredicatesPass> {
public:
explicit AliasOpaquePredicatesPass(AliasOpParams params = {},
std::uint64_t seed = 0x1337)
: params_(params), engine_(core::Xoshiro256pp::fromSeed(seed)) {}

llvm::PreservedAnalyses run(llvm::Function &F,
llvm::FunctionAnalysisManager &AM);
static bool isRequired() { return true; }

private:
AliasOpParams params_;
Expand Down
Loading
Loading