use super::*;
use rlx_tpu::TpuExecutable;
pub struct TpuBackend;
impl Backend for TpuBackend {
fn supported_ops(&self) -> &'static [rlx_ir::OpKind] {
rlx_tpu::SUPPORTED_OPS
}
fn compile(&self, graph: Graph, options: &CompileOptions) -> Box<dyn ExecutableGraph> {
let graph =
rlx_opt::rlx_autodiff::decompose_backward_ops_except(graph, rlx_tpu::SUPPORTED_OPS);
let graph = rlx_opt::legalize_or_rewrite_for_backend_with_config(
graph,
rlx_tpu::SUPPORTED_OPS,
options.kernel_dispatch,
)
.unwrap_or_else(|errors| {
panic!("{}", rlx_opt::format_legalize_error("tpu", &errors));
});
use rlx_opt::pass::Pass as _;
let policy = options
.policy
.clone()
.unwrap_or(rlx_opt::PrecisionPolicy::AutoMixedBf16);
let graph = rlx_opt::AutoMixedPrecision::new(policy).run(graph);
let _ = options.dce;
let _ = options.constant_folding;
Box::new(TpuExecutableWrapper {
inner: TpuExecutable::compile_rng_with_param_bytes(
graph,
options.rng,
options.quant_param_bindings.as_ref(),
),
})
}
}
struct TpuExecutableWrapper {
inner: TpuExecutable,
}
unsafe impl Send for TpuExecutableWrapper {}
impl ExecutableGraph for TpuExecutableWrapper {
fn capabilities(&self) -> crate::ExecutableCapabilities {
crate::ExecutableCapabilities {
clone: true,
typed_io: true,
..crate::ExecutableCapabilities::NONE
}
}
fn set_param(&mut self, name: &str, data: &[f32]) {
self.inner.set_param(name, data);
}
fn run(&mut self, inputs: &[(&str, &[f32])]) -> Vec<Vec<f32>> {
self.inner.run(inputs)
}
fn set_param_typed(&mut self, name: &str, data: &[u8], dtype: rlx_ir::DType) {
if dtype == rlx_ir::DType::F32 {
let n = data.len() / 4;
let s = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const f32, n) };
self.inner.set_param(name, s);
} else {
let f32_buf = super::widen_bytes_to_f32(data, dtype);
self.inner.set_param(name, &f32_buf);
}
}
fn run_typed(
&mut self,
inputs: &[(&str, &[u8], rlx_ir::DType)],
) -> Vec<(Vec<u8>, rlx_ir::DType)> {
let mut owned: Vec<(String, Vec<f32>)> = Vec::with_capacity(inputs.len());
for (name, data, dt) in inputs {
let v = super::widen_bytes_to_f32(data, *dt);
owned.push((name.to_string(), v));
}
let refs: Vec<(&str, &[f32])> = owned
.iter()
.map(|(n, d)| (n.as_str(), d.as_slice()))
.collect();
let dtypes = self.inner.output_dtypes();
let outs = self.inner.run(&refs);
outs.into_iter()
.zip(
dtypes
.into_iter()
.chain(std::iter::repeat(rlx_ir::DType::F32)),
)
.map(|(v, dt)| (super::narrow_f32_to_bytes(&v, dt), dt))
.collect()
}
fn clone_box(&self) -> Box<dyn ExecutableGraph> {
Box::new(TpuExecutableWrapper {
inner: self.inner.clone_for_cache(),
})
}
}