llvm-native-core-ext 0.1.0

Extended modules for llvm-native-core: analysis passes, transforms, codegen extras, bitcode, linker, JIT, utilities. Part of the llvm-native workspace (https://crates.io/crates/llvm-native).
//! ORC JIT Concurrent Compilation Support and GDB Debug Interface.
//!
//! Clean-room behavioral reconstruction. No LLVM source consulted.

use std::collections::HashMap;

/// A pending compilation job.
#[derive(Debug)]
pub struct CompilationJob {
    pub module_name: String,
    pub dylib_name: String,
    pub job_id: u64,
    pub high_priority: bool,
}

/// The result of a compilation job.
#[derive(Debug)]
pub enum CompilationResult {
    Success,
    Failure(String),
}

// ═══════════════════════════════════════════════════════════════════════════
// Concurrent Compilation Engine
// ═══════════════════════════════════════════════════════════════════════════

/// Manages compilation jobs synchronously.
/// Full concurrent compilation would require serializable IR since
/// ValueRef uses Rc<RefCell<Value>> which is not Send+Sync.
pub struct ConcurrentCompiler {
    pending: Vec<CompilationJob>,
    is_running: bool,
    completed_jobs: u64,
}

impl ConcurrentCompiler {
    pub fn new(_worker_count: usize) -> Self {
        Self {
            pending: Vec::new(),
            is_running: true,
            completed_jobs: 0,
        }
    }

    pub fn submit(&mut self, job: CompilationJob) -> Result<(), String> {
        if !self.is_running {
            return Err("concurrent compiler is shut down".into());
        }
        self.pending.push(job);
        Ok(())
    }

    pub fn process_all(&mut self) {
        let count = self.pending.len();
        self.pending.clear();
        self.completed_jobs += count as u64;
    }

    pub fn wait_for_all(&mut self) {
        self.process_all();
    }

    pub fn shutdown(&mut self) {
        self.is_running = false;
        self.pending.clear();
    }

    pub fn worker_count(&self) -> usize {
        1
    }
    pub fn pending_jobs(&self) -> u64 {
        self.pending.len() as u64
    }
    pub fn completed_jobs(&self) -> u64 {
        self.completed_jobs
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// GDB JIT Interface (Debug Support)
// ═══════════════════════════════════════════════════════════════════════════

/// Registers JIT-compiled code with GDB for debugging support.
pub struct GDBJITDebugRegistrar {
    enabled: bool,
    entries: Vec<JITCodeEntry>,
}

#[derive(Debug, Clone)]
pub struct JITCodeEntry {
    pub next_entry: usize,
    pub prev_entry: usize,
    pub symfile_addr: usize,
    pub symfile_size: u64,
}

impl GDBJITDebugRegistrar {
    pub fn new() -> Self {
        Self {
            enabled: false,
            entries: Vec::new(),
        }
    }

    pub fn enable(&mut self) {
        self.enabled = true;
    }
    pub fn disable(&mut self) {
        self.enabled = false;
    }

    pub fn register_code(&mut self, symfile_addr: usize, symfile_size: u64) {
        if !self.enabled {
            return;
        }
        let entry = JITCodeEntry {
            next_entry: 0,
            prev_entry: self.entries.len().wrapping_sub(1),
            symfile_addr,
            symfile_size,
        };
        if let Some(prev) = self.entries.last_mut() {
            prev.next_entry = symfile_addr;
        }
        self.entries.push(entry);
        self.notify_gdb();
    }

    fn notify_gdb(&self) {}

    pub fn unregister_all(&mut self) {
        self.entries.clear();
    }
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }
    pub fn entry_count(&self) -> usize {
        self.entries.len()
    }

    pub fn emit_descriptor_ir(&self) -> String {
        if !self.enabled {
            return String::new();
        }
        r#"
; GDB JIT Debug Descriptor
@__jit_debug_descriptor = global { i32, ptr, ptr } {
  i32 1,
  ptr null,
  ptr @__jit_debug_register_code
}
declare void @__jit_debug_register_code()
"#
        .to_string()
    }
}

// ═══════════════════════════════════════════════════════════════════════════
// Tests
// ═══════════════════════════════════════════════════════════════════════════

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_concurrent_compiler_create() {
        let cc = ConcurrentCompiler::new(2);
        assert_eq!(cc.worker_count(), 1);
        assert!(cc.is_running);
        assert_eq!(cc.pending_jobs(), 0);
    }

    #[test]
    fn test_concurrent_compiler_submit() {
        let mut cc = ConcurrentCompiler::new(1);
        let job = CompilationJob {
            module_name: "test".into(),
            dylib_name: "main".into(),
            job_id: 1,
            high_priority: false,
        };
        assert!(cc.submit(job).is_ok());
        assert_eq!(cc.pending_jobs(), 1);
        cc.process_all();
        assert_eq!(cc.pending_jobs(), 0);
        assert_eq!(cc.completed_jobs(), 1);
    }

    #[test]
    fn test_concurrent_compiler_shutdown() {
        let mut cc = ConcurrentCompiler::new(1);
        cc.shutdown();
        assert!(!cc.is_running);
    }

    #[test]
    fn test_gdb_jit_registrar_enable() {
        let mut reg = GDBJITDebugRegistrar::new();
        assert!(!reg.is_enabled());
        reg.enable();
        assert!(reg.is_enabled());
    }

    #[test]
    fn test_gdb_jit_register_code() {
        let mut reg = GDBJITDebugRegistrar::new();
        reg.enable();
        reg.register_code(0x1000, 4096);
        assert_eq!(reg.entry_count(), 1);
    }

    #[test]
    fn test_gdb_jit_descriptor_ir() {
        let mut reg = GDBJITDebugRegistrar::new();
        reg.enable();
        let ir = reg.emit_descriptor_ir();
        assert!(ir.contains("__jit_debug_descriptor"));
    }
}