# Release Build

This document describes how to build optimized release binaries of Wasot. For [optional features](./optional_features.md) that can be included, see the features documentation. For [testing](./testing.md) the build, see the testing guide. For [memory](./memory_analysis.md) performance analysis, see the memory analysis document.

First prio is speed, second is size. We assume infinite compile time.


This pipeline combines **Static Linking (Musl)** + **Mimalloc** + **PGO** + **BOLT**.

### Prerequisites

You need a Linux environment with the following installed:

1. **Rust Nightly** with musl target.
2. **Musl tools:** `musl-tools` (apt) or `musl-devel` (dnf).
3. **LLVM BOLT:** `llvm-bolt` and `merge-fdata` for binary optimization (optional but recommended).
   * *Note:* The script uses Rust's bundled `llvm-profdata` (via `llvm-tools` component) to avoid LLVM version mismatches.

```bash
# Ubuntu/Debian
sudo apt install musl-tools llvm-bolt

# Install Rust nightly, musl target, and llvm-tools
rustup toolchain install nightly
rustup +nightly target add x86_64-unknown-linux-musl
rustup +nightly component add llvm-tools
```

---

### Step 1: `Cargo.toml` Configuration

Configure your profile to prioritize speed (`opt-level = 3`) while removing bloat that doesn't help performance (`panic = "abort"`).

```toml
[dependencies]
# Replace the slow default Musl allocator with Mimalloc
mimalloc = { version = "0.1", default-features = false, optional = true }

[features]
optimized-alloc = ["mimalloc"]

[profile.release]
opt-level = 3         # Max Speed. Enables vectorization and unrolling.
lto = "fat"           # Max Speed. Cross-crate optimization.
codegen-units = 1     # Max Speed. Slower compile, better optimization.
panic = "abort"       # Size & Speed. Removes unwinding overhead.
strip = true          # Size. Removes debug symbols.

# Profile for PGO+BOLT builds (strip=false needed for BOLT symbols)
[profile.release-pgo]
inherits = "release"
strip = false         # IMPORTANT: Do not strip yet! BOLT needs symbols.
```

---

### Step 2: Code Modification (Allocator)

In your `src/main.rs`, inject **Mimalloc** conditionally. This is critical because the default allocator in `musl` is optimized for size and static correctness, making it very slow for high-performance apps.

```rust
// Use mimalloc allocator for optimized builds (faster than musl's default)
#[cfg(feature = "optimized-alloc")]
use mimalloc::MiMalloc;

#[cfg(feature = "optimized-alloc")]
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;

fn main() {
    // Your application logic
}
```

---

### Step 3: The Optimization Pipeline (Shell Script)

Save this sequence as a script or run commands manually.

**Context:** We use the generic `x86_64-unknown-linux-musl` target to ensure it runs on any Intel/AMD Linux machine.

#### Phase A: Profile-Guided Optimization (PGO)

We first teach the Rust compiler which functions are actually used.

> **Note:** PGO with musl and `-Z build-std` has known linker compatibility issues with the LLVM profiler runtime. The script omits `-Z build-std` for PGO builds to work around this.

```bash
# 0. Cleanup previous builds
cargo clean
rm -rf /tmp/pgo-data /tmp/bolt-data
mkdir -p /tmp/pgo-data /tmp/bolt-data

# 1. Build PGO Instrumented Binary
# Note: We don't use -Z build-std with PGO+musl due to profiler runtime linker issues
echo "--- Building PGO Instrumentation Binary ---"
RUSTFLAGS="-Cprofile-generate=/tmp/pgo-data" \
cargo +nightly build \
    --profile release-pgo \
    --target x86_64-unknown-linux-musl \
    --features optimized-alloc

# 2. Train PGO
# Run the binary with a REALISTIC workload.
# The longer and more varied the run, the better the optimization.
echo "--- Training PGO ---"

# Primary workload: Generated syntax tests (263 comprehensive parser tests)
# These are generated from tests/wasot_syntax_tests.md by scripts/generate_syntax_tests.py
RUSTFLAGS="-Cprofile-generate=/tmp/pgo-data" \
cargo +nightly test \
    --test generated_syntax_tests \
    --no-run \
    --profile release-pgo \
    --target x86_64-unknown-linux-musl \
    --features optimized-alloc

# Find and run the test binary
TEST_BIN=$(find ./target/x86_64-unknown-linux-musl/release-pgo/deps -name "generated_syntax_tests-*" -type f -executable | head -1)
"$TEST_BIN" --test-threads=1

# Secondary workload: CLI commands (exercises output formatting code)
./target/x86_64-unknown-linux-musl/release-pgo/wasot xml-listener examples/demo.wasot --pretty >/dev/null
./target/x86_64-unknown-linux-musl/release-pgo/wasot to-jsonl examples/demo.wasot -o /tmp/out.jsonl
./target/x86_64-unknown-linux-musl/release-pgo/wasot select examples/selector-showcase.wasot -s "paragraph" >/dev/null

# 3. Merge PGO Data
# Use Rust's llvm-profdata to match the compiler's LLVM version
~/.rustup/toolchains/nightly-*/lib/rustlib/*/bin/llvm-profdata merge \
    -o /tmp/pgo-data/merged.profdata /tmp/pgo-data

# 4. Apply PGO & Prepare for BOLT
# We apply the profile data.
# CRITICAL: We add `-C link-arg=--emit-relocs` so BOLT can reorganize the binary later.
echo "--- Building PGO-Optimized Binary ---"
RUSTFLAGS="-Cprofile-use=/tmp/pgo-data/merged.profdata -Clink-arg=-Wl,--emit-relocs" \
cargo +nightly build \
    --profile release-pgo \
    --target x86_64-unknown-linux-musl \
    --features optimized-alloc
```

#### Phase B: BOLT (Binary Optimization and Layout Tool)

Now that Rust has done its best, we use BOLT to physically rearrange the assembly instructions inside the binary to minimize CPU cache misses.

> **Note:** BOLT requires `libbolt_rt_instr.a` which is not included in Ubuntu's `llvm-bolt` package. If missing, the script falls back to a PGO-only build which still provides significant optimization.

```bash
# Define paths for clarity
SRC_BIN="./target/x86_64-unknown-linux-musl/release-pgo/wasot"
INST_BIN="./target/x86_64-unknown-linux-musl/release-pgo/wasot_bolt_inst"
FINAL_BIN="./release/wasot"

mkdir -p ./release

# 5. Instrument Binary with BOLT
# This creates a new version of the binary that records hardware jumps.
echo "--- Instrumenting for BOLT ---"
llvm-bolt "$SRC_BIN" -o "$INST_BIN" \
    -instrument \
    --instrumentation-file-append-pid \
    --instrumentation-file=/tmp/bolt-data/prof

# 6. Train BOLT
# Run this instrumented binary with the same workload.
echo "--- Training BOLT ---"
"$INST_BIN" xml-listener examples/demo.wasot --pretty >/dev/null
"$INST_BIN" to-jsonl examples/demo.wasot -o /tmp/out.jsonl
"$INST_BIN" select examples/selector-showcase.wasot -s "paragraph" >/dev/null

# 7. Convert BOLT Profile
# BOLT needs to convert the raw instrumentation data into its own format.
# We merge all generated profiles (in case the app forked or ran multiple times).
echo "--- Merging BOLT Profiles ---"
merge-fdata /tmp/bolt-data/prof* > /tmp/bolt-data/merged.fdata

# 8. Optimize with BOLT
# This creates the final executable.
# -reorder-blocks=ext-tsp: Advanced algorithm to keep hot code paths straight.
# -split-functions: Splits hot and cold parts of a function.
# -split-all-cold: Moves all cold code to a remote part of the binary (Huge cache win).
# -dyno-stats: Shows you the improvement stats at the end.
echo "--- Running Final BOLT Optimization ---"
llvm-bolt "$SRC_BIN" -o "$FINAL_BIN" \
    -data /tmp/bolt-data/merged.fdata \
    -reorder-blocks=ext-tsp \
    -reorder-functions=hfsort \
    -split-functions \
    -split-all-cold \
    -icf=1 \
    -dyno-stats
```

#### Phase C: Final Cleanup (Strip)

We delayed stripping symbols until now because BOLT requires them to understand the binary structure.

```bash
# 9. Strip the final binary
# Removes debug symbols to minimize disk size.
echo "--- Stripping Final Binary ---"
strip --strip-all "$FINAL_BIN"

echo "Done. Your optimized binary is at: $FINAL_BIN"
```

### Summary of What Just Happened

1. **Mimalloc** fixed the heap allocation bottlenecks of static Linux binaries.
2. **Rust PGO** taught the compiler to inline the right functions and predict branches based on real usage.
3. **LLVM BOLT** physically reorganized the machine code so that the CPU executes linear instructions without jumping around memory (improving Instruction Cache hits).
4. **Split-All-Cold** moved unused code (error handling, panic paths) far away from the hot loop, reducing the effective size of the "hot" binary in memory.

---

### Automated Build Script

The entire pipeline is automated in `scripts/build/build_release.sh`. This script handles prerequisites checking, toolchain installation, and all build phases.

```bash
# Full PGO + BOLT optimized build
./scripts/build/build_release.sh

# With clean (removes previous build artifacts first)
./scripts/build/build_release.sh --clean

# Simple release build (no PGO/BOLT, much faster)
./scripts/build/build_release.sh --simple

# PGO only, skip BOLT phase
./scripts/build/build_release.sh --skip-bolt
```

The script will:
- Check and install missing Rust toolchain components (nightly, musl target, rust-src)
- Verify system tools are available (musl-gcc, llvm-profdata, llvm-bolt, merge-fdata)
- Run the complete optimization pipeline with progress output
- Verify the final binary works correctly
- Output the final binary to `release/wasot`
