use std::time::Instant;
use foundation::LogitValue;
use crate::moe_prefill_pipeline::routed_pipeline_banks;
use crate::real_forward::RealForwardRunner;
use crate::real_forward_dispatch::{encode_embed_any, encode_gemv_any};
use crate::real_forward_types::{RealForwardError, MAX_PREFILL_BATCH};
use crate::real_forward_utils::norm_view;
impl RealForwardRunner {
pub(crate) fn prefill_chunk_real_gpt_oss(
&mut self,
tokens: &[i32],
start_position: usize,
logits: &mut [LogitValue],
) -> Result<(), RealForwardError> {
if tokens.is_empty() {
return Err(RealForwardError::Unsupported(
"prefill_chunk called with an empty chunk".to_string(),
));
}
if self.batched_gemv_prefill {
return Err(RealForwardError::Unsupported(
"TURBOSPARK_BATCHED_GEMV is not wired for this family: the M-row \
resident GEMM exists in the gemma4 chunked driver alone \
(INT4-affine), and this driver keeps every resident GEMV per \
token"
.to_string(),
));
}
let mut offset = 0usize;
while offset < tokens.len() {
let take = (tokens.len() - offset).min(MAX_PREFILL_BATCH);
let last = offset + take == tokens.len();
self.prefill_micro_batch_gpt_oss(
&tokens[offset..offset + take],
start_position + offset,
last,
logits,
)?;
offset += take;
}
Ok(())
}
fn prefill_micro_batch_gpt_oss(
&mut self,
tokens: &[i32],
start_position: usize,
want_head: bool,
logits: &mut [LogitValue],
) -> Result<(), RealForwardError> {
let started = Instant::now();
let result =
self.prefill_micro_batch_gpt_oss_inner(tokens, start_position, want_head, logits);
self.phases.calls += tokens.len() as u64;
self.phases.total_nanos += started.elapsed().as_nanos() as u64;
result
}
fn prefill_micro_batch_gpt_oss_inner(
&mut self,
tokens: &[i32],
start_position: usize,
want_head: bool,
logits: &mut [LogitValue],
) -> Result<(), RealForwardError> {
let arch = self.arch.clone();
let hidden = arch.hidden_size as usize;
let moe_inter = arch.moe_intermediate_size as u32;
let vocab = arch.vocab_size as usize;
let num_experts = arch.num_experts as usize;
let top_k = arch.top_k_experts as usize;
let gpu_err = RealForwardError::Gpu;
let m = tokens.len();
if start_position != self.kv.position() {
return Err(RealForwardError::Unsupported(format!(
"non-sequential chunk start {start_position}; KV cache is at {}",
self.kv.position()
)));
}
if let Some(&bad) = tokens.iter().find(|&&t| (t as usize) >= vocab) {
return Err(RealForwardError::Unsupported(format!(
"token id {bad} outside vocab {vocab}"
)));
}
let banks = if self.routed_pipeline {
routed_pipeline_banks(self.expert_cache_slots, top_k)
} else {
1
};
if self.routed_batch_prefill {
let context = &mut self.context;
self.real_gpt_oss
.as_mut()
.expect("real gpt-oss state present")
.ensure_batched(context, &arch)?;
}
let embed_name = "language_model.model.embed_tokens.weight";
let mut pass = self
.context
.begin_pass_labeled("gpt-oss chunk cb1 (attn+router)");
for (t, &token) in tokens.iter().enumerate() {
encode_embed_any(
&mut self.context,
&pass,
&self.weights,
&self.index,
embed_name,
(&self.scratch.x, (t * hidden * 2) as u64),
token as u32,
hidden as u32,
vocab,
1.0,
)?;
}
let mut pending_routed: Option<gpu::CommittedPass> = None;
for layer in 0..arch.num_layers as usize {
for t in 0..m {
let position = start_position + t;
self.encode_gpt_oss_layer_attn_and_router(
&pass,
layer,
position,
t,
hidden,
num_experts,
)?;
}
let cb1 = pass.commit();
let t_wait = Instant::now();
self.phases.cb1_gpu_nanos += (cb1.wait_with_gpu_time() * 1e9) as u64;
self.phases.gpu_wait_nanos += t_wait.elapsed().as_nanos() as u64;
self.retire_routed(&mut pending_routed);
if self.routed_batch_prefill {
pending_routed = Some(self.encode_gpt_oss_layer_routed_moe_batched(
layer,
hidden,
moe_inter,
num_experts,
top_k,
m,
)?);
} else {
self.encode_gpt_oss_layer_routed_moe_pipelined(
&mut pending_routed,
layer,
hidden,
moe_inter,
num_experts,
top_k,
banks,
m,
)?;
}
pass = self
.context
.begin_pass_labeled("gpt-oss chunk cb1 (attn+router)");
}
self.retire_routed(&mut pending_routed);
if want_head {
pass.relabel("gpt-oss final cb (head)");
let last_off = ((m - 1) * hidden * 2) as u64;
let final_norm = norm_view(
&self.weights,
&self.index,
"language_model.model.norm.weight",
hidden,
)?;
let rms_eps = self
.real_gpt_oss
.as_ref()
.expect("real gpt-oss state present")
.rms_eps;
gpu::encode_rms_norm_bf16w(
&mut self.context,
&pass,
(&self.scratch.x, last_off),
final_norm,
(&self.scratch.normed, 0),
hidden as u32,
rms_eps,
)
.map_err(gpu_err)?;
encode_gemv_any(
&mut self.context,
&pass,
&self.weights,
&self.index,
"language_model.lm_head.weight",
vocab,
hidden,
(&self.scratch.normed, 0),
(&self.scratch.logits, 0),
)?;
} else {
pass.relabel("gpt-oss chunk cb (no head)");
}
let t_wait = Instant::now();
self.phases.final_cb_gpu_nanos += (pass.commit_and_wait_with_gpu_time() * 1e9) as u64;
self.phases.final_wait_nanos += t_wait.elapsed().as_nanos() as u64;
self.kv.advance_by(m);
let skip_head = !want_head;
let last_position = start_position + m - 1;
if let Some(capture) = self.resid_capture.as_mut() {
capture.record_pass(last_position, skip_head);
}
if !want_head {
return Ok(());
}
if vocab != logits.len() {
return Err(RealForwardError::Unsupported(format!(
"vocab mismatch: model has {}, caller expected {}",
vocab,
logits.len()
)));
}
gpu::read_buffer_f16_into(&self.scratch.logits, 0, logits);
Ok(())
}
}