use std::collections::HashSet;
use std::os::raw::c_void;
use std::panic::AssertUnwindSafe;
use crate::engine::{
copy_out_raw_delta, dim_i32, mlx_dtype_from_onnx, read_ctx_input_raw, rope_row_key, DeltaWrite,
DynInput, MlxError, NodeDesc, OutRef, Plan, ShapeMode, Slot, Src, SynthRope, TracePayload,
TranslationContext,
};
use crate::mlx::{self, Array, Closure, VectorArray};
use crate::sys::mlx as mlxsys;
use crate::sys::ort;
pub fn compile_enabled(has_control_flow: bool) -> bool {
let killed = std::env::var_os("ONNX_GENAI_MLX_NO_COMPILE")
.map(|v| v != "0" && !v.is_empty())
.unwrap_or(false);
!has_control_flow && !killed
}
fn is_general_compile_unsafe(op_type: &str) -> bool {
matches!(
op_type,
"GroupQueryAttention"
| "Attention"
| "MultiHeadAttention"
| "SparseAttention"
| "Det"
| "NonZero"
| "Unique"
)
}
pub fn general_enabled(has_control_flow: bool, nodes: &[NodeDesc]) -> bool {
if !compile_enabled(has_control_flow) {
return false;
}
if std::env::var_os("MLX_EP_NO_GENERAL_COMPILE")
.map(|v| v != "0" && !v.is_empty())
.unwrap_or(false)
{
return false;
}
!nodes.iter().any(|n| is_general_compile_unsafe(&n.op_type))
}
pub fn prefill_enabled(has_control_flow: bool, nodes: &[NodeDesc]) -> bool {
if !compile_enabled(has_control_flow) {
return false;
}
if std::env::var_os("MLX_EP_NO_PREFILL_COMPILE")
.map(|v| v != "0" && !v.is_empty())
.unwrap_or(false)
{
return false;
}
nodes.iter().any(|n| n.op_type == "GroupQueryAttention")
}
pub fn detect_seq_len(
api: *const ort::OrtApi,
kctx: *mut ort::OrtKernelContext,
plan: &Plan,
) -> Option<i32> {
for node in &plan.nodes {
for inp in &node.inputs {
if inp.source == Src::CtxInput && !inp.constant && inp.name == "input_ids" {
if let Ok((_d, shape, _t)) = read_ctx_input_raw(api, kctx, inp.ctx_index) {
return shape.last().map(|&d| d as i32);
}
}
}
}
None
}
fn detect_shared_kv(
plan: &Plan,
api: *const ort::OrtApi,
kctx: *mut ort::OrtKernelContext,
) -> Option<(bool, i32)> {
let s = detect_seq_len(api, kctx, plan).unwrap_or(1);
let mask_ctx_index = plan
.nodes
.iter()
.flat_map(|n| n.inputs.iter())
.find(|inp| inp.source == Src::CtxInput && inp.name == "attention_mask")
.map(|inp| inp.ctx_index as i32)
.unwrap_or(-1);
for node in &plan.nodes {
if node.op_type != "GroupQueryAttention" || node.inputs.len() < 7 {
continue;
}
let past_k = &node.inputs[3];
if past_k.source != Src::CtxInput {
continue;
}
let cap = match read_ctx_input_raw(api, kctx, past_k.ctx_index) {
Ok((_d, shape, _t)) => *shape.get(2)? as i32,
Err(_) => continue,
};
let ts = &node.inputs[6];
let total = if ts.source == Src::CtxInput && !ts.constant {
match read_ctx_input_raw(api, kctx, ts.ctx_index) {
Ok((data, _shape, _t)) if !data.is_null() => unsafe { *(data as *const i32) },
_ => continue,
}
} else if mask_ctx_index >= 0 {
match read_ctx_input_raw(api, kctx, mask_ctx_index as usize) {
Ok((_d, shape, _t)) => *shape.get(1)? as i32,
Err(_) => continue,
}
} else {
continue;
};
return Some((cap > total - s, mask_ctx_index));
}
None
}
fn mk_slice(
a: mlxsys::mlx_array,
start: &[i32],
stop: &[i32],
stream: mlxsys::mlx_stream,
) -> Result<Array, MlxError> {
let stride = vec![1i32; start.len()];
let mut res = unsafe { mlxsys::mlx_array_new() };
let rc = unsafe {
mlxsys::mlx_slice(
&mut res,
a,
start.as_ptr(),
start.len(),
stop.as_ptr(),
stop.len(),
stride.as_ptr(),
stride.len(),
stream,
)
};
if rc != 0 {
unsafe { mlxsys::mlx_array_free(res) };
return Err("mlx_slice failed".to_string());
}
Ok(Array::from_raw(res))
}
fn mk_concat(
a: mlxsys::mlx_array,
b: mlxsys::mlx_array,
axis: i32,
stream: mlxsys::mlx_stream,
) -> Result<Array, MlxError> {
let mut v = VectorArray::new();
v.append(a);
v.append(b);
let mut res = unsafe { mlxsys::mlx_array_new() };
let rc = unsafe { mlxsys::mlx_concatenate_axis(&mut res, v.as_raw(), axis, stream) };
if rc != 0 {
unsafe { mlxsys::mlx_array_free(res) };
return Err("mlx_concatenate_axis failed".to_string());
}
Ok(Array::from_raw(res))
}
fn shape_of(a: mlxsys::mlx_array) -> Vec<i32> {
let nd = unsafe { mlxsys::mlx_array_ndim(a) };
let sh = unsafe { mlxsys::mlx_array_shape(a) };
(0..nd).map(|i| unsafe { *sh.add(i) }).collect()
}
pub fn try_compiled(
plan_ptr: *mut Plan,
slot: Slot,
api: *const ort::OrtApi,
kctx: *mut ort::OrtKernelContext,
stream: mlxsys::mlx_stream,
) -> Result<bool, MlxError> {
let cfg = slot.get(unsafe { &*plan_ptr }).config;
if !slot.get(unsafe { &*plan_ptr }).enabled {
return Ok(false);
}
if !slot.get(unsafe { &*plan_ptr }).attempted {
let terminal = {
let _phase = crate::trace::tracer().phase("compile");
build_closure(plan_ptr, slot, api, kctx, stream)
};
if terminal {
slot.get_mut(unsafe { &mut *plan_ptr }).attempted = true;
}
}
if !slot.get(unsafe { &*plan_ptr }).valid {
return Ok(false);
}
if cfg.shape_mode == ShapeMode::ShapeKeyed {
let plan = unsafe { &*plan_ptr };
for di in &slot.get(plan).dyn_inputs {
let (_data, shape, _dtype) = read_ctx_input_raw(api, kctx, di.ctx_index)?;
if shape.iter().any(|&d| d == 0) {
return Ok(false);
}
}
}
let mut arena: Vec<Array> = Vec::new();
let mut input = VectorArray::new();
{
let plan = unsafe { &*plan_ptr };
for di in &slot.get(plan).dyn_inputs {
let (data, shape, dtype) = read_ctx_input_raw(api, kctx, di.ctx_index)?;
let ishape: Vec<i32> = shape.iter().map(|&d| dim_i32(d)).collect::<Result<_, _>>()?;
let arr = Array::from_data_managed(data, &ishape, mlx_dtype_from_onnx(dtype));
input.append(arr.as_raw());
arena.push(arr);
}
}
let mut past: i32 = 0;
let mut s_new: i32 = 0;
if cfg.rope_as_data {
let s = detect_seq_len(api, kctx, unsafe { &*plan_ptr }).unwrap_or(1);
s_new = s;
past = {
let plan = unsafe { &*plan_ptr };
let c = slot.get(plan);
if c.shared_kv && c.mask_ctx_index >= 0 {
let (_d, shape, _t) =
read_ctx_input_raw(api, kctx, c.mask_ctx_index as usize)?;
(*shape.get(1).unwrap_or(&0) as i32) - s
} else {
let idx = c.rope_past_ctx_index as usize;
let axis = c.rope_past_axis as usize;
let (_d, shape, _t) = read_ctx_input_raw(api, kctx, idx)?;
if axis < shape.len() {
shape[axis] as i32
} else {
0
}
}
};
let synth = slot.get(unsafe { &*plan_ptr }).synth_ropes.clone();
for sr in &synth {
let cache_raw = match unsafe { &*plan_ptr }.cache.get(&sr.cache_name) {
Some(a) => a.as_raw(),
None => return Ok(false), };
let half = shape_of(cache_raw).get(1).copied().unwrap_or(0);
if half == 0 {
return Ok(false);
}
let row = mk_slice(cache_raw, &[past, 0], &[past + s, half], stream)?; let full = mk_concat(row.as_raw(), row.as_raw(), 1, stream)?; input.append(full.as_raw());
arena.push(row);
arena.push(full);
}
if slot.get(unsafe { &*plan_ptr }).shared_kv {
let vp = [past];
let arr = Array::from_data(
vp.as_ptr() as *const std::os::raw::c_void,
&[1],
mlxsys::mlx_dtype__MLX_INT32,
);
input.append(arr.as_raw());
arena.push(arr);
}
}
unsafe {
if let Some(p) = slot.get_mut(&mut *plan_ptr).payload.as_mut() {
p.kctx = kctx;
}
}
let closure = match slot.get_mut(unsafe { &mut *plan_ptr }).closure.take() {
Some(c) => c,
None => return Ok(false),
};
let apply_res = closure.apply(&input);
slot.get_mut(unsafe { &mut *plan_ptr }).closure = Some(closure);
let outs = match apply_res {
Ok(v) => v,
Err(_) => {
slot.get_mut(unsafe { &mut *plan_ptr }).valid = false; return Ok(false);
}
};
let tr = crate::trace::tracer();
let eval_t0 = if tr.active() { Some(std::time::Instant::now()) } else { None };
let _eval_region = tr.eval_region();
if mlx::eval(&outs).is_err() {
slot.get_mut(unsafe { &mut *plan_ptr }).valid = false;
return Ok(false);
}
drop(_eval_region);
if let Some(t0) = eval_t0 {
tr.record_phase("eval", t0.elapsed());
}
let ext_len = slot.get(unsafe { &*plan_ptr }).ext_outputs.len();
if outs.size() != ext_len {
slot.get_mut(unsafe { &mut *plan_ptr }).valid = false;
return Ok(false);
}
let kv_present: Vec<(String, usize)> = if cfg.delta_copyout {
slot.get(unsafe { &*plan_ptr }).kv_present_names.clone()
} else {
Vec::new()
};
for i in 0..ext_len {
let a = outs.get(i);
let o: OutRef = slot.get(unsafe { &*plan_ptr }).ext_outputs[i].clone();
let delta = kv_present
.iter()
.find(|(name, _)| *name == o.name)
.and_then(|(_, past_ctx)| {
read_ctx_input_raw(api, kctx, *past_ctx)
.ok()
.map(|(p, _, _)| DeltaWrite {
axis: 2,
offset: past as i64,
count: s_new as i64,
alias_ptr: p as usize,
})
});
copy_out_raw_delta(api, kctx, &o, a.as_raw(), delta)?;
}
Ok(true)
}
fn build_closure(
plan_ptr: *mut Plan,
slot: Slot,
api: *const ort::OrtApi,
kctx: *mut ort::OrtKernelContext,
stream: mlxsys::mlx_stream,
) -> bool {
let cfg = slot.get(unsafe { &*plan_ptr }).config;
let mut dyn_inputs: Vec<DynInput> = Vec::new();
let mut ext_outputs: Vec<OutRef> = Vec::new();
let mut synth_ropes: Vec<SynthRope> = Vec::new();
let mut rope_past: i32 = -1;
{
let plan = unsafe { &*plan_ptr };
let mut seen: HashSet<String> = HashSet::new();
for node in &plan.nodes {
for inp in &node.inputs {
if inp.source == Src::CtxInput && !inp.constant && seen.insert(inp.name.clone()) {
dyn_inputs.push(DynInput {
name: inp.name.clone(),
ctx_index: inp.ctx_index,
});
if cfg.rope_as_data && rope_past < 0 && inp.name.contains(".key") {
rope_past = inp.ctx_index as i32;
}
}
}
}
for node in &plan.nodes {
for o in &node.outputs {
if o.external {
ext_outputs.push(o.clone());
}
}
}
if cfg.rope_as_data {
let mut synth_seen: HashSet<String> = HashSet::new();
for node in &plan.nodes {
if node.op_type != "GroupQueryAttention" || node.inputs.len() < 9 {
continue;
}
let do_rotary = !node.ints.contains_key("do_rotary")
|| node.ints.get("do_rotary").copied() != Some(0);
if !do_rotary {
continue;
}
for idx in [7usize, 8usize] {
let nm = &node.inputs[idx].name;
if synth_seen.insert(nm.clone()) {
synth_ropes.push(SynthRope {
key: rope_row_key(nm),
cache_name: nm.clone(),
});
}
}
}
}
}
if dyn_inputs.is_empty() || ext_outputs.is_empty() {
return true;
}
if cfg.rope_as_data && (rope_past < 0 || synth_ropes.is_empty()) {
return true; }
let mut shared_kv = false;
let mut mask_ctx_index = -1;
if cfg.rope_as_data {
let hd = match read_ctx_input_raw(api, kctx, rope_past as usize) {
Ok((_d, shape, _t)) => shape.last().copied().unwrap_or(0) as i32,
Err(_) => 0,
};
let (half, cache_resident) = {
let plan = unsafe { &*plan_ptr };
match plan.cache.get(&synth_ropes[0].cache_name) {
Some(a) => {
let sh = a.shape();
(if sh.len() >= 2 { sh[1] as i32 } else { 0 }, true)
}
None => (0, false),
}
};
if !cache_resident {
return false; }
if hd == 0 || half == 0 || 2 * half != hd {
return true; }
}
if cfg.kv_alias {
let detected = detect_shared_kv(unsafe { &*plan_ptr }, api, kctx).unwrap_or((false, -1));
shared_kv = detected.0;
mask_ctx_index = detected.1;
}
unsafe {
let c = slot.get_mut(&mut *plan_ptr);
c.shared_kv = shared_kv;
c.mask_ctx_index = mask_ctx_index;
c.dyn_inputs = dyn_inputs;
c.ext_outputs = ext_outputs;
c.synth_ropes = synth_ropes;
c.rope_past_ctx_index = rope_past;
c.rope_past_axis = 2;
c.payload = Some(Box::new(TracePayload {
plan: plan_ptr,
ort_api: api,
kctx,
stream,
slot,
}));
}
let payload_ptr: *mut c_void = unsafe {
slot.get_mut(&mut *plan_ptr).payload.as_mut().unwrap().as_mut() as *mut TracePayload
as *mut c_void
};
let shapeless = matches!(cfg.shape_mode, ShapeMode::Shapeless);
let base = Closure::new_func_payload(trace_thunk, payload_ptr);
match Closure::compile(&base, shapeless) {
Ok(compiled) => {
let c = slot.get_mut(unsafe { &mut *plan_ptr });
c.closure = Some(compiled);
c.valid = true;
}
Err(_) => { }
}
true
}
extern "C" fn trace_thunk(
out: *mut mlxsys::mlx_vector_array,
input: mlxsys::mlx_vector_array,
payload: *mut c_void,
) -> std::os::raw::c_int {
let result = std::panic::catch_unwind(AssertUnwindSafe(|| trace_body(out, input, payload)));
match result {
Ok(Ok(())) => 0,
Ok(Err(e)) => {
if crate::trace::tracer().active() {
eprintln!("[rust-mlx-ep] compiled trace failed ({e}); falling back to eager");
}
1
}
Err(payload) => {
let detail = crate::panic_payload_message(&payload);
eprintln!(
"[onnxruntime-mlx] WARNING: compiled trace panicked ({detail}); falling back to the eager path (result still correct)"
);
1
}
}
}
fn trace_body(
out: *mut mlxsys::mlx_vector_array,
input: mlxsys::mlx_vector_array,
payload: *mut c_void,
) -> Result<(), MlxError> {
let pl = unsafe { &mut *(payload as *mut TracePayload) };
let plan_ptr = pl.plan;
let api = pl.ort_api;
let kctx = pl.kctx;
let stream = pl.stream;
let slot = pl.slot;
let (cfg, dyn_inputs, synth_ropes, ext_outputs, shared_kv) = {
let c = slot.get(unsafe { &*plan_ptr });
(
c.config,
c.dyn_inputs.clone(),
c.synth_ropes.clone(),
c.ext_outputs.clone(),
c.shared_kv,
)
};
let mut narrow = false;
let mut static_valid_past = 0i32;
if matches!(cfg.shape_mode, ShapeMode::ShapeKeyed) && cfg.rope_as_data && shared_kv {
let s = detect_seq_len(api, kctx, unsafe { &*plan_ptr }).unwrap_or(1);
let mask_idx = slot.get(unsafe { &*plan_ptr }).mask_ctx_index;
if mask_idx >= 0 {
if let Ok((_d, shape, _t)) = read_ctx_input_raw(api, kctx, mask_idx as usize) {
let vp = (*shape.get(1).unwrap_or(&0) as i32) - s;
if vp >= 0 {
narrow = true;
static_valid_past = vp;
}
}
}
}
let (res_raw, arena, kv_present) = {
let plan = unsafe { &mut *plan_ptr };
let mut tc = TranslationContext::new(plan, api, kctx, stream);
if cfg.rope_as_data {
tc.set_compiled_trace(shared_kv, narrow, static_valid_past);
} else {
tc.set_general_trace();
}
let ndyn = dyn_inputs.len();
for (i, di) in dyn_inputs.iter().enumerate() {
let mut a = unsafe { mlxsys::mlx_array_new() };
unsafe { mlxsys::mlx_vector_array_get(&mut a, input, i) };
let raw = tc.keep(Array::from_raw(a));
tc.seed(di.name.clone(), raw);
}
if cfg.rope_as_data {
for (j, sr) in synth_ropes.iter().enumerate() {
let mut a = unsafe { mlxsys::mlx_array_new() };
unsafe { mlxsys::mlx_vector_array_get(&mut a, input, ndyn + j) };
let raw = tc.keep(Array::from_raw(a));
tc.seed(sr.key.clone(), raw);
}
if shared_kv {
let mut a = unsafe { mlxsys::mlx_array_new() };
unsafe { mlxsys::mlx_vector_array_get(&mut a, input, ndyn + synth_ropes.len()) };
let raw = tc.keep(Array::from_raw(a));
tc.seed(crate::engine::GQA_VALID_PAST_KEY.to_string(), raw);
}
}
let res = tc.run_trace(&ext_outputs, cfg.contiguous_outputs)?;
let kv_present = tc.take_compiled_kv_present();
let arena = tc.take_arena();
(res.into_raw(), arena, kv_present)
};
unsafe {
let c = slot.get_mut(&mut *plan_ptr);
c.trace_transient.extend(arena);
if cfg.delta_copyout {
c.kv_present_names = kv_present;
}
*out = res_raw;
}
Ok(())
}