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 self.compile_module(GraphModule::from_graph(graph))
96 .expect("compile MIR graph through fusion pipeline")
97 }
98
99 fn compile_deferred(&self, graph: Graph, options: crate::CompileOptions) -> CompiledGraph {
101 let backend = self.create_backend();
102 let inner = Box::new(crate::deferred::DeferredExecutable::new(
103 graph,
104 backend,
105 options,
106 self.device,
107 ));
108 CompiledGraph::new(inner, self.device)
109 }
110
111 pub fn compile_graph(&self, graph: Graph) -> CompiledGraph {
113 self.compile(graph)
114 }
115
116 pub fn compile_with(&self, graph: Graph, options: &crate::CompileOptions) -> CompiledGraph {
121 let graph = match options.scaled_quant {
126 Some(cfg) => {
127 rlx_opt::rlx_compile::scaled_quant_insert::insert_scaled_matmul(graph, cfg)
128 }
129 None => graph,
130 };
131 if rlx_ir::dynamic::has_dynamic_dims(&graph) && !self.coreml_native_flex() {
132 return self.compile_deferred(graph, options.clone());
133 }
134 self.compile_module_with(GraphModule::from_graph(graph), options)
135 .expect("compile MIR graph through fusion pipeline")
136 }
137
138 pub fn compile_hir(&self, hir: HirModule) -> Result<CompiledGraph, rlx_ir::hir::LowerError> {
140 self.compile_hir_with(hir, &self.default_options())
141 }
142
143 pub fn compile_hir_with(
145 &self,
146 hir: HirModule,
147 options: &crate::CompileOptions,
148 ) -> Result<CompiledGraph, rlx_ir::hir::LowerError> {
149 let backend = self.create_backend();
150 let executable = backend.compile_hir(hir, self.device, options)?;
151 Ok(CompiledGraph::new(executable, self.device))
152 }
153
154 pub fn compile_module(
156 &self,
157 module: GraphModule,
158 ) -> Result<CompiledGraph, rlx_ir::hir::LowerError> {
159 self.compile_module_with(module, &self.default_options())
160 }
161
162 pub fn compile_module_with(
164 &self,
165 module: GraphModule,
166 options: &crate::CompileOptions,
167 ) -> Result<CompiledGraph, rlx_ir::hir::LowerError> {
168 let backend = self.create_backend();
169 let executable = backend.compile_module(module, self.device, options)?;
170 Ok(CompiledGraph::new(executable, self.device))
171 }
172
173 fn default_options(&self) -> crate::CompileOptions {
174 let opts = crate::CompileOptions::new().precision(self.precision);
175 match &self.policy {
176 Some(p) => opts.policy(p.clone()),
177 None => opts,
178 }
179 }
180
181 fn coreml_native_flex(&self) -> bool {
183 #[cfg(all(
184 feature = "coreml",
185 target_vendor = "apple",
186 not(target_os = "watchos")
187 ))]
188 {
189 self.device == Device::Ane
190 && std::env::var("RLX_COREML_NATIVE_FLEX").ok().as_deref() == Some("1")
191 }
192 #[cfg(not(all(
193 feature = "coreml",
194 target_vendor = "apple",
195 not(target_os = "watchos")
196 )))]
197 {
198 false
199 }
200 }
201
202 fn create_backend(&self) -> Box<dyn Backend> {
203 crate::registry::backend_for(self.device).unwrap_or_else(|| {
207 panic!(
208 "no backend registered for device {} — enable feature `{}` \
209 (or call `rlx_runtime::register_backend` for an external backend)",
210 self.device,
211 feature_name(self.device)
212 )
213 })
214 }
215}
216
217fn feature_name(device: Device) -> &'static str {
218 match device {
219 Device::Cpu => "cpu",
220 Device::Metal => "metal",
221 Device::Mlx => "mlx",
222 Device::Ane => "ane",
223 Device::Cuda => "cuda",
224 Device::Rocm => "rocm",
225 Device::OneApi => "oneapi",
226 Device::Tpu => "tpu",
227 Device::Hexagon => "qnn",
228 Device::Gpu => "gpu",
229 Device::Vulkan => "vulkan",
230 Device::OpenGl => "opengl",
231 Device::DirectX => "directx",
232 Device::WebGpu => "webgpu",
233 }
234}