1use crate::backend::Backend;
19use crate::compiled::CompiledGraph;
20use crate::precision::Precision;
21use rlx_driver::Device;
22use rlx_ir::Graph;
23use rlx_ir::GraphModule;
24use rlx_ir::hir::HirModule;
25use rlx_opt::PrecisionPolicy;
26
27pub struct Session {
29 device: Device,
30 precision: Precision,
31 policy: Option<PrecisionPolicy>,
35}
36
37impl Session {
38 pub fn new(device: Device) -> Self {
43 Self::new_with_precision(device, Precision::F32)
44 }
45
46 pub fn new_with_precision(device: Device, precision: Precision) -> Self {
49 assert!(
50 crate::device_ext::is_available(device),
51 "device {} is not available — enable the `{}` Cargo feature",
52 device,
53 feature_name(device)
54 );
55 Self {
56 device,
57 precision,
58 policy: None,
59 }
60 }
61
62 pub fn with_policy(mut self, policy: PrecisionPolicy) -> Self {
66 self.policy = Some(policy);
67 self
68 }
69
70 pub fn device(&self) -> Device {
71 self.device
72 }
73 pub fn precision(&self) -> Precision {
74 self.precision
75 }
76 pub fn policy(&self) -> Option<&PrecisionPolicy> {
77 self.policy.as_ref()
78 }
79
80 pub fn compile(&self, graph: Graph) -> CompiledGraph {
92 if rlx_ir::dynamic::has_dynamic_dims(&graph) && !self.coreml_native_flex() {
93 return self.compile_deferred(graph, self.default_options());
94 }
95 let opts = self.default_options();
96 if opts.cache_param_invariant
97 && let Some(staged) = self.try_hoist_param_invariant(&graph, &opts)
98 {
99 return staged;
100 }
101 self.compile_module(GraphModule::from_graph(graph))
102 .expect("compile MIR graph through fusion pipeline")
103 }
104
105 fn try_hoist_param_invariant(
110 &self,
111 graph: &Graph,
112 options: &crate::CompileOptions,
113 ) -> Option<CompiledGraph> {
114 let split = rlx_compile::split_param_invariant(graph)?;
115 let mut sub = options.clone();
116 sub.cache_param_invariant = false;
117 let prepare = self.compile_with(split.prepare, &sub);
118 let main = self.compile_with(split.main, &sub);
119 Some(main.with_staging(
120 prepare,
121 split.boundary,
122 split.prepare_params,
123 split.main_params,
124 ))
125 }
126
127 fn compile_deferred(&self, graph: Graph, options: crate::CompileOptions) -> CompiledGraph {
129 let backend = self.create_backend();
130 let inner = Box::new(crate::deferred::DeferredExecutable::new(
131 graph,
132 backend,
133 options,
134 self.device,
135 ));
136 CompiledGraph::new(inner, self.device)
137 }
138
139 pub fn compile_graph(&self, graph: Graph) -> CompiledGraph {
141 self.compile(graph)
142 }
143
144 pub fn compile_with(&self, graph: Graph, options: &crate::CompileOptions) -> CompiledGraph {
149 let graph = match options.scaled_quant {
154 Some(cfg) => {
155 rlx_opt::rlx_compile::scaled_quant_insert::insert_scaled_matmul(graph, cfg)
156 }
157 None => graph,
158 };
159 if rlx_ir::dynamic::has_dynamic_dims(&graph) && !self.coreml_native_flex() {
160 return self.compile_deferred(graph, options.clone());
161 }
162 if options.cache_param_invariant
163 && let Some(staged) = self.try_hoist_param_invariant(&graph, options)
164 {
165 return staged;
166 }
167 self.compile_module_with(GraphModule::from_graph(graph), options)
168 .expect("compile MIR graph through fusion pipeline")
169 }
170
171 pub fn compile_hir(&self, hir: HirModule) -> Result<CompiledGraph, rlx_ir::hir::LowerError> {
173 self.compile_hir_with(hir, &self.default_options())
174 }
175
176 pub fn compile_hir_with(
178 &self,
179 hir: HirModule,
180 options: &crate::CompileOptions,
181 ) -> Result<CompiledGraph, rlx_ir::hir::LowerError> {
182 let backend = self.create_backend();
183 let executable = backend.compile_hir(hir, self.device, options)?;
184 Ok(CompiledGraph::new(executable, self.device))
185 }
186
187 pub fn compile_module(
189 &self,
190 module: GraphModule,
191 ) -> Result<CompiledGraph, rlx_ir::hir::LowerError> {
192 self.compile_module_with(module, &self.default_options())
193 }
194
195 pub fn compile_module_with(
197 &self,
198 module: GraphModule,
199 options: &crate::CompileOptions,
200 ) -> Result<CompiledGraph, rlx_ir::hir::LowerError> {
201 let backend = self.create_backend();
202 let executable = backend.compile_module(module, self.device, options)?;
203 Ok(CompiledGraph::new(executable, self.device))
204 }
205
206 fn default_options(&self) -> crate::CompileOptions {
207 let opts = crate::CompileOptions::new().precision(self.precision);
208 match &self.policy {
209 Some(p) => opts.policy(p.clone()),
210 None => opts,
211 }
212 }
213
214 fn coreml_native_flex(&self) -> bool {
216 #[cfg(all(
217 feature = "coreml",
218 target_vendor = "apple",
219 not(target_os = "watchos")
220 ))]
221 {
222 self.device == Device::Ane
223 && std::env::var("RLX_COREML_NATIVE_FLEX").ok().as_deref() == Some("1")
224 }
225 #[cfg(not(all(
226 feature = "coreml",
227 target_vendor = "apple",
228 not(target_os = "watchos")
229 )))]
230 {
231 false
232 }
233 }
234
235 fn create_backend(&self) -> Box<dyn Backend> {
236 crate::registry::backend_for(self.device).unwrap_or_else(|| {
240 panic!(
241 "no backend registered for device {} — enable feature `{}` \
242 (or call `rlx_runtime::register_backend` for an external backend)",
243 self.device,
244 feature_name(self.device)
245 )
246 })
247 }
248}
249
250fn feature_name(device: Device) -> &'static str {
251 match device {
252 Device::Cpu => "cpu",
253 Device::Metal => "metal",
254 Device::Mlx => "mlx",
255 Device::Ane => "ane",
256 Device::Cuda => "cuda",
257 Device::Rocm => "rocm",
258 Device::OneApi => "oneapi",
259 Device::Tpu => "tpu",
260 Device::Hexagon => "qnn",
261 Device::Gpu => "gpu",
262 Device::Vulkan => "vulkan",
263 Device::OpenGl => "opengl",
264 Device::DirectX => "directx",
265 Device::WebGpu => "webgpu",
266 }
267}