Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

JIT Compilation

The jit feature flag enables Just-In-Time compilation of HBAC rules via Cranelift. It adds a hybrid JIT/interpreter runtime as an additional optimization layer in the evaluation pipeline.

Enabling

[dependencies]
hbac-rs = { version = "0.2", features = ["jit"] }
#![allow(unused)]
fn main() {
let mut policy = HbacPolicy::new();
policy.load_rules(rules);
policy.enable_jit();

for request in requests {
    policy.evaluate(&request);
}
}

enable_jit() returns false if Cranelift is not available for the current architecture (only x86_64 is supported).

How It Works

The JIT runtime profiles rule evaluations and compiles hot rules to native code after a threshold of 100 evaluations. Rules that are not hot or cannot be compiled fall back to the standard decision tree interpreter.

flowchart TD
    EVAL[evaluate request] --> JIT{JIT-compiled<br/>deny rules?}
    JIT -->|match| DENY[return deny]
    JIT -->|no match| INTERP[decision tree<br/>interpreter]
    INTERP --> RESULT[return result]

    PROFILE[profiler records<br/>evaluation count] -.-> HOT{count >= 100?}
    HOT -->|yes| COMPILE[compile via Cranelift]
    COMPILE --> CACHE[store in jit_cache]

The runtime checks JIT-compiled deny rules first for early exit, then delegates to the interpreter for the full evaluation.

Rule Classification

The compiler classifies each rule into a pattern that determines the compilation strategy:

PatternCriteriaCompilation
CategoryAllAll three dimensions are category=allNative Cranelift codegen (constant return)
SingleUserOne user name, any host/serviceOptimized interpreter (direct string comparison)
GroupMatchUser groups specified, any host/serviceOptimized interpreter (set membership check)
ComplexMultiple users, or other patternsStandard interpreter (not compiled)

Only CategoryAll rules produce actual native machine code. SingleUser and GroupMatch use pattern-specific data cached at classification time for faster interpreter evaluation, but do not generate native code.

Internals

Components

  • JitCompiler – Cranelift-based compiler. Owns the JITModule, declares and finalizes functions.
  • HotRuleProfiler – LRU-based evaluation counter (256 entries). Tracks per-rule evaluation counts and identifies rules above the JIT threshold.
  • JitRuntime – Orchestrates profiling, compilation, and evaluation. Maintains a jit_cache mapping rule names to compiled evaluators.
  • CompiledRule – Implements the RuleEvaluator trait. Holds either a native function pointer (CategoryAll) or pattern-specific data for the optimized interpreter fallback.

RuleEvaluator Trait

#![allow(unused)]
fn main() {
pub trait RuleEvaluator: Send + Sync {
    fn evaluate(&self, request: &HbacRequest) -> bool;
    fn debug_info(&self) -> String;
}
}

All compiled rules implement this trait. CompiledRule dispatches to the native function pointer when available, otherwise uses the cached pattern data.

Safety

CompiledRule contains a raw function pointer to JIT-compiled code. The pointer is safe because:

  • It is obtained from JITModule::get_finalized_function() after finalization
  • The backing JITModule is owned by JitCompiler, which is owned by the same JitRuntime that owns the CompiledRule in its jit_cache
  • The compiled code is read-only after finalization (Send + Sync)

Statistics

#![allow(unused)]
fn main() {
let stats = policy.jit_stats();
println!("total: {}", stats.total_rules);
println!("compiled: {}", stats.jit_compiled);
println!("hot: {}", stats.hot_rules);
}

jit_compile_hot() manually triggers compilation of all rules that have crossed the profiling threshold:

#![allow(unused)]
fn main() {
policy.jit_compile_hot();
}

Performance Impact

See Performance Results for measured JIT impact at each scale. Summary:

  • Cached workloads: no measurable difference (LRU cache dominates)
  • Uncached workloads: ~8-12% throughput improvement
  • Build time: no consistent overhead

JIT is worth enabling when uncached or low-hit-rate workloads dominate. For typical SSSD deployments with high cache hit rates, it provides no benefit.

Binary Size

The jit feature adds ~500 KB from Cranelift dependencies.

See Also