vortex-jit 0.3.0

Rust bindings for the VORTEX JIT compiler — a profile-guided optimizing JIT with input-shape-keyed profiles, phase partitioning, ensemble aggregation, T1 code persistence, and append-only crash-safe profile patching.
// build.rs — Compile the VORTEX C library and generate Rust FFI bindings.
//
// This script:
//   1. Compiles all C source files from ../src/ into a static library
//   2. Runs bindgen on wrapper.h to generate Rust FFI type/function declarations
//   3. Links the static library

use std::path::PathBuf;

fn main() {
    // ---- Find the VORTEX source directory ----
    let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    let vortex_src = PathBuf::from(&manifest_dir).join("..").join("src");
    let vortex_build = PathBuf::from(&manifest_dir).join("..").join("build");

    // ---- Collect all C source files ----
    let mut c_files = Vec::new();
    collect_c_files(&vortex_src, &mut c_files);

    // ---- Determine compile flags (matching the CMake build) ----
    let mut cc_build = cc::Build::new();
    cc_build
        .compiler("gcc")
        .flag("-std=gnu17")
        .flag("-O3")
        .flag("-fPIC")
        .flag("-DNDEBUG")
        // Warnings (suppressed to match the C build)
        .flag("-Wno-unused-variable")
        .flag("-Wno-unused-function")
        .flag("-Wno-unused-but-set-variable")
        .flag("-Wno-unused-parameter")
        .flag("-Wno-sign-compare")
        .flag("-Wno-discarded-qualifiers")
        .flag("-Wno-incompatible-pointer-types");

    // Include paths
    cc_build.include(&vortex_src);
    cc_build.include(&vortex_build);

    // Feature flags
    cc_build.define("VORTEX_ENABLE_SOTA", None);
    cc_build.define("VORTEX_ENABLE_ASSERTIONS", None);
    cc_build.define("VORTEX_ENABLE_VERIFY", None);
    cc_build.define("VORTEX_ENABLE_PROFILING", None);

    // Config values (matching CMakeCache.txt defaults)
    cc_build.define("VORTEX_CACHE_MAX_SIZE", Some("268435456"));
    cc_build.define("VORTEX_T1_THRESHOLD", Some("1000"));
    cc_build.define("VORTEX_T2_THRESHOLD", Some("10000"));
    cc_build.define("VORTEX_COMPILE_THREADS", Some("0"));

    // Add all source files
    for f in &c_files {
        cc_build.file(f);
    }

    // Compile and link
    cc_build.compile("vortex");

    // ---- Generate FFI bindings with bindgen ----
    let wrapper = PathBuf::from(&manifest_dir).join("wrapper.h");

    let bindings = bindgen::Builder::default()
        .header(wrapper.to_str().unwrap())
        .clang_arg(format!("-I{}", vortex_src.display()))
        .clang_arg(format!("-I{}", vortex_build.display()))
        .clang_arg("-isystem/usr/lib/gcc/x86_64-linux-gnu/14/include")
        .clang_arg("-isystem/usr/include")
        .clang_arg("-DVORTEX_ENABLE_SOTA")
        .clang_arg("-DVORTEX_ENABLE_ASSERTIONS")
        .clang_arg("-DVORTEX_ENABLE_VERIFY")
        .clang_arg("-DVORTEX_ENABLE_PROFILING")
        .clang_arg("-DVORTEX_CACHE_MAX_SIZE=268435456")
        .clang_arg("-DVORTEX_T1_THRESHOLD=1000")
        .clang_arg("-DVORTEX_T2_THRESHOLD=10000")
        .clang_arg("-DVORTEX_COMPILE_THREADS=0")
        // Allowlist the VORTEX API
        .allowlist_type("vtx_.*")
        .allowlist_function("vtx_.*")
        .allowlist_var("VTX_.*")
        .allowlist_var("VORTEX_.*")
        .allowlist_var("VT_OP_.*")
        .allowlist_var("VT_IC_.*")
        .allowlist_var("VTX_GC_.*")
        .allowlist_var("VTX_PHASE_.*")
        .allowlist_var("VTX_CONFIDENCE_.*")
        .allowlist_var("VTX_PROMOTION_.*")
        .allowlist_var("VTX_RECOMP_.*")
        .allowlist_var("VTX_ENSEMBLE_.*")
        .allowlist_var("VTX_INPUT_SHAPE_.*")
        .allowlist_var("VTX_SIZE_BIN_.*")
        .allowlist_var("VTX_T1_CACHE_.*")
        .allowlist_var("VTX_PATCH_.*")
        .allowlist_var("VTX_TAG_.*")
        .allowlist_var("VTX_NAN_.*")
        .allowlist_var("VTX_SMI_.*")
        .allowlist_var("VTX_POLY_.*")
        .allowlist_var("VTX_PROFILE_.*")
        .allowlist_var("VTX_MAX_.*")
        .allowlist_var("VTX_INLINE_.*")
        .allowlist_var("VTX_GUARD_.*")
        .allowlist_var("VTX_VALUE_.*")
        .allowlist_var("VTX_FEEDBACK_.*")
        .allowlist_var("VTX_LOOP_.*")
        .allowlist_var("VTX_SIDE_.*")
        .allowlist_var("VTX_STITCH_.*")
        .allowlist_var("VTX_TIER_.*")
        .allowlist_var("VTX_ORCHESTRATOR_.*")
        .allowlist_var("VTX_PHASE_PARTITION_.*")
        .allowlist_var("VTX_SHAPE_DISPATCH_.*")
        .allowlist_var("VTX_TRIP_.*")
        .allowlist_var("VTX_TYPE_FEEDBACK_.*")
        .allowlist_var("VTX_TYPE_STABILITY_.*")
        .allowlist_var("VTX_SHAPE_STABILITY_.*")
        .allowlist_var("VTX_TYPE_GUARD_.*")
        .allowlist_var("VTX_CACHE_.*")
        .allowlist_var("VTX_ARENA_.*")
        .allowlist_var("VTX_FRAME_.*")
        .allowlist_var("VTX_INTERP_.*")
        .allowlist_type("VT_OP_.*")
        .allowlist_type("VT_IC_.*")
        .allowlist_type("VTX_GC_.*")
        .allowlist_type("VTX_TIER_.*")
        // Layout tests cause issues with opaque types
        .layout_tests(false)
        .generate_comments(true)
        .generate_inline_functions(true)
        .derive_debug(true)
        .derive_copy(true)
        .derive_default(true)
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .generate()
        .expect("Failed to generate bindings");

    // Write bindings to OUT_DIR
    let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Failed to write bindings");

    // Tell cargo to invalidate when C files change
    for f in &c_files {
        println!("cargo:rerun-if-changed={}", f.display());
    }
    println!("cargo:rerun-if-changed={}", wrapper.display());
}

/// Recursively collect all .c files from a directory.
fn collect_c_files(dir: &PathBuf, files: &mut Vec<PathBuf>) {
    if let Ok(entries) = std::fs::read_dir(dir) {
        for entry in entries.flatten() {
            let path = entry.path();
            if path.is_dir() {
                collect_c_files(&path, files);
            } else if path.extension().and_then(|e| e.to_str()) == Some("c") {
                // Skip main_new.c — it has a main() function
                if path.file_name().and_then(|n| n.to_str()) != Some("main_new.c") {
                    files.push(path);
                }
            }
        }
    }
}