1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! Legacy MCJIT-style wrapper (inkwell's plain `ExecutionEngine`, one per
//! `Module`): build an `ExecutionEngine` for a lowered `Module` and recover
//! a raw function pointer / typed extern-C shim by name.
//!
//! **BINDING CORRECTION 4 (Task 24, `docs/superpowers/plans/2026-09-23-
//! wave-A-codegen.md`):** `Jit`/`ExecutionEngine` is not the production backend.
//! It is kept only where an existing test still references it (`tests/
//! jit_add.rs`, `tests/power.rs`'s pre-Task-24 merge test, `tests/
//! session_parallel.rs`'s pre-Task-24 multi-module JIT test). New code must
//! use [`crate::orc::JitBackend`], the real `LLJIT`/`JITDylib`-based backend
//! CG-062/CG-064/CG-124 ask for.
use inkwell::OptimizationLevel;
use inkwell::execution_engine::ExecutionEngine;
use inkwell::module::Module;
use inkwell::targets::{InitializationConfig, Target};
use inkwell::values::FunctionValue;
pub struct Jit<'ctx> {
ee: ExecutionEngine<'ctx>,
}
impl<'ctx> Jit<'ctx> {
pub fn from_module(module: Module<'ctx>) -> Result<Self, String> {
// Required before any JIT engine can be created (idempotent;
// `create_jit_execution_engine` also calls this internally, but
// calling it here keeps this constructor correct standalone).
// Verified against inkwell 0.10 + LLVM 22.1.8 on this host.
Target::initialize_native(&InitializationConfig::default())?;
let ee = module
.create_jit_execution_engine(OptimizationLevel::Default)
.map_err(|e| e.to_string())?;
Ok(Jit { ee })
}
pub fn get_fn2_i32(&self, name: &str) -> Option<extern "C" fn(i32, i32) -> i32> {
self.get_raw(name)
.map(|p| unsafe { std::mem::transmute::<*const (), extern "C" fn(i32, i32) -> i32>(p) })
}
/// CG-125: retrieves a compiled process kernel's callable function
/// pointer by its mangled (LLVM) name -- `Some(fn_ptr)` when `mangled`
/// names a function this `Jit`'s module was built with (whether or not
/// it has actually run yet), `None` for an unknown name.
///
/// **Superseded-ABI reconciliation (this crate's own "superseded-ABI
/// note", `docs/superpowers/plans/2026-09-26-wave-A2-slice.md`'s global
/// constraints section):** CG-125's literal ledger text asks for a
/// `SvResumeStatus`-returning signature (`docs/design/codegen.md`'s
/// superseded §9 draft) -- that type does not exist anywhere in this
/// tree. This function instead returns the frozen, ARCHITECTURE §5 ABI's
/// own [`sv_sim_abi::ProcessFn`] (`extern "C" fn(SimHandle, *mut u8, u32)
/// -> EvalResult`), the same reconciliation `sv-sim-codegen::
/// process_lower::lower_kernel_fn`'s own doc comment and
/// `sv-sim-sim::sched::kernel_adapter::SV_KERNEL_ABI_RECONCILIATION`
/// already record for the rest of this same ABI boundary -- never the
/// literal superseded struct name.
pub fn get_process_fn(&self, mangled: &str) -> Option<sv_sim_abi::ProcessFn> {
self.get_raw(mangled)
.map(|p| unsafe { std::mem::transmute::<*const (), sv_sim_abi::ProcessFn>(p) })
}
// Corrected vs. plan: `ExecutionEngine::get_function_address` is a safe
// fn in inkwell 0.10 (not `unsafe fn`), so wrapping the call itself in
// `unsafe` is an unnecessary block that trips `unused_unsafe` under
// `#![deny(warnings)]`. The genuinely unsafe operation stays the
// `mem::transmute` in `get_fn2_i32`.
pub fn get_raw(&self, name: &str) -> Option<*const ()> {
self.ee
.get_function_address(name)
.ok()
.map(|a| a as *const ())
}
// Deviation from the plan (Task S1, discovered writing its own tests,
// not in the plan's `Files:` list for `jit.rs`): the accessor-call
// tests need to bind a declared-but-undefined `extern "C"` accessor
// symbol (e.g. `sv_get_scalar`) to a Rust test-double function before
// JIT-executing anything that calls it -- there is no real STATE arena
// for the JIT to resolve that symbol against otherwise. inkwell's
// `ExecutionEngine::add_global_mapping` is exactly this binding; `Jit`
// had no way to reach it since `ee` is a private field. This thin
// wrapper is the minimal addition that unblocks that test shape and is
// equally what the real `sv-sim-bin` JIT path will need later to bind
// the compiled kernel's declared accessor calls to
// `sv-sim-sim::state::accessor_impl`'s real `extern "C" fn`s.
pub fn add_global_mapping(&self, function: FunctionValue<'ctx>, addr: usize) {
self.ee.add_global_mapping(&function, addr);
}
}