1use std::fmt::Display;
2
3use cubecl_core::{
4 Compiler, WgpuCompilationOptions,
5 prelude::{CompiledKernel, KernelDefinition},
6 server::{ComputeServer, LaunchError, ResourceLimitError},
7};
8#[cfg(feature = "msl")]
9use cubecl_cpp::shared::MslComputeKernel;
10use cubecl_environment::backtrace::BackTrace;
11use cubecl_ir::DeviceProperties;
12use cubecl_runtime::compiler::CompilationError;
13use derive_more::derive::From;
14
15#[cfg(feature = "spirv")]
16use crate::ParamsTransfer;
17use crate::{CompilerInfo, WgpuServer};
18
19use super::wgsl;
20
21#[cfg(feature = "msl")]
22pub use cubecl_cpp::MslCompiler;
23#[cfg(feature = "spirv")]
24pub use cubecl_spirv::SpirvCompiler;
25pub use wgsl::WgslCompiler;
26
27#[allow(clippy::large_enum_variant)]
33#[derive(Debug, Clone)]
34pub enum AutoCompiler {
35 Wgsl(WgslCompiler),
37 #[cfg(feature = "spirv")]
39 SpirV(cubecl_spirv::SpirvCompiler),
40 #[cfg(feature = "msl")]
42 Msl(MslCompiler),
43}
44
45#[derive(From)]
47#[allow(clippy::large_enum_variant)]
48pub enum AutoRepresentation {
49 Wgsl(wgsl::ComputeShader),
51 #[cfg(feature = "spirv")]
53 SpirV(cubecl_spirv::SpirvKernel),
54 #[cfg(feature = "msl")]
56 Msl(MslComputeKernel),
57}
58
59#[derive(From, Clone, Copy)]
61#[allow(clippy::large_enum_variant)]
62pub enum AutoRepresentationRef<'a> {
63 Wgsl(&'a wgsl::ComputeShader),
65 #[cfg(feature = "spirv")]
67 SpirV(&'a cubecl_spirv::SpirvKernel),
68 #[cfg(feature = "msl")]
70 Msl(&'a MslComputeKernel),
71}
72
73#[cfg(feature = "spirv")]
74impl AutoRepresentation {
75 pub fn as_spirv(&self) -> Option<&cubecl_spirv::SpirvKernel> {
77 match self {
78 AutoRepresentation::SpirV(repr) => Some(repr),
79 _ => None,
80 }
81 }
82}
83
84#[cfg(feature = "msl")]
85impl AutoRepresentation {
86 pub fn as_msl(&self) -> Option<&MslComputeKernel> {
88 match self {
89 AutoRepresentation::Msl(repr) => Some(repr),
90 _ => None,
91 }
92 }
93}
94
95impl AutoRepresentation {
96 pub fn as_ref(&self) -> AutoRepresentationRef<'_> {
98 match self {
99 AutoRepresentation::Wgsl(compute_shader) => AutoRepresentationRef::Wgsl(compute_shader),
100 #[cfg(feature = "spirv")]
101 AutoRepresentation::SpirV(spirv_kernel) => AutoRepresentationRef::SpirV(spirv_kernel),
102 #[cfg(feature = "msl")]
103 AutoRepresentation::Msl(compute_shader) => AutoRepresentationRef::Msl(compute_shader),
104 }
105 }
106}
107
108impl Display for AutoRepresentation {
109 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result {
110 match self {
111 AutoRepresentation::Wgsl(compute_shader) => compute_shader.fmt(f),
112 #[cfg(feature = "spirv")]
113 AutoRepresentation::SpirV(spirv_kernel) => spirv_kernel.fmt(f),
114 #[cfg(feature = "msl")]
115 AutoRepresentation::Msl(compute_shader) => compute_shader.fmt(f),
116 }
117 }
118}
119
120impl Compiler for AutoCompiler {
121 type Representation = AutoRepresentation;
122
123 type CompilationOptions = WgpuCompilationOptions;
124
125 fn compile(
126 &mut self,
127 kernel: KernelDefinition,
128 compilation_options: &Self::CompilationOptions,
129 ) -> Result<Self::Representation, CompilationError> {
130 let kernel = match self {
131 AutoCompiler::Wgsl(wgsl_compiler) => {
132 Compiler::compile(wgsl_compiler, kernel, compilation_options)?.into()
133 }
134 #[cfg(feature = "spirv")]
135 AutoCompiler::SpirV(spirv_compiler) => {
136 Compiler::compile(spirv_compiler, kernel, compilation_options)?.into()
137 }
138 #[cfg(feature = "msl")]
139 AutoCompiler::Msl(msl_compiler) => {
140 use cubecl_cpp;
142 let compilation_options = cubecl_cpp::shared::CompilationOptions::default();
143 Compiler::compile(msl_compiler, kernel, &compilation_options)?.into()
144 }
145 };
146
147 Ok(kernel)
148 }
149
150 fn extension(&self) -> &'static str {
151 match self {
152 AutoCompiler::Wgsl(_) => "wgsl",
153 #[cfg(feature = "spirv")]
154 AutoCompiler::SpirV(_) => "spv",
155 #[cfg(feature = "msl")]
156 AutoCompiler::Msl(_) => "msl",
157 }
158 }
159}
160
161impl WgpuCompiler for AutoCompiler {
162 fn init(backend: wgpu::Backend, options: &WgpuCompilationOptions) -> Self {
163 let _ = options; match backend {
165 #[cfg(feature = "spirv")]
166 wgpu::Backend::Vulkan if options.supports_vulkan_compiler => {
167 AutoCompiler::SpirV(Default::default())
168 }
169 #[cfg(feature = "msl")]
170 wgpu::Backend::Metal if options.supports_msl_compiler => {
171 AutoCompiler::Msl(Default::default())
172 }
173 _ => AutoCompiler::Wgsl(Default::default()),
174 }
175 }
176
177 fn compile_kernel(
178 &mut self,
179 server: &mut WgpuServer<AutoCompiler>,
180 kernel: <WgpuServer<AutoCompiler> as ComputeServer>::Kernel,
181 definition: KernelDefinition,
182 ) -> Result<CompiledKernel<Self>, CompilationError> {
183 match self {
184 AutoCompiler::Wgsl(_) => kernel.compile(definition, self, &server.compilation_options),
185 #[cfg(feature = "spirv")]
186 AutoCompiler::SpirV(_) => crate::vulkan::compile(self, server, kernel, definition),
187 #[cfg(feature = "msl")]
188 AutoCompiler::Msl(_) => kernel.compile(definition, self, &server.compilation_options),
189 }
190 }
191
192 fn lang_tag(&self) -> &'static str {
193 match self {
194 AutoCompiler::Wgsl(_) => "wgsl",
195 #[cfg(feature = "spirv")]
196 AutoCompiler::SpirV(_) => "spirv",
197 #[cfg(feature = "msl")]
198 AutoCompiler::Msl(_) => "msl",
199 }
200 }
201
202 fn validate_ir(
203 &self,
204 repr: &Option<Self::Representation>,
205 props: &DeviceProperties,
206 ) -> Result<(), LaunchError> {
207 let shared_bytes = repr.as_ref().map(|repr| match repr {
208 AutoRepresentation::Wgsl(repr) => repr.shared_memory_size,
209 #[cfg(feature = "msl")]
210 AutoRepresentation::Msl(repr) => repr.shared_memory_size,
211 #[cfg(feature = "spirv")]
212 AutoRepresentation::SpirV(repr) => repr.shared_size,
213 });
214 check_shared_memory(shared_bytes, props)
215 }
216
217 fn normalize_repr(
218 &self,
219 repr: Option<Self::Representation>,
220 ) -> (CompilerInfo, Option<AutoRepresentation>) {
221 let compiler_info = match &repr {
222 #[cfg(feature = "spirv")]
223 Some(AutoRepresentation::SpirV(repr)) => CompilerInfo::Vulkan {
224 params_transfer: match repr.immediate_size {
225 Some(_) => ParamsTransfer::Immediate,
226 None => ParamsTransfer::Uniform,
227 },
228 },
229 #[cfg(feature = "msl")]
230 Some(AutoRepresentation::Msl(_)) => CompilerInfo::Metal,
231 Some(AutoRepresentation::Wgsl(_)) => CompilerInfo::WGSL,
232 None => CompilerInfo::None,
233 };
234
235 (compiler_info, repr)
236 }
237}
238
239impl WgpuCompiler for WgslCompiler {
240 fn init(_backend: wgpu::Backend, _options: &WgpuCompilationOptions) -> Self {
241 Self
242 }
243
244 fn compile_kernel(
245 &mut self,
246 server: &mut WgpuServer<Self>,
247 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
248 definition: KernelDefinition,
249 ) -> Result<CompiledKernel<Self>, CompilationError> {
250 kernel.compile(definition, self, &server.compilation_options)
251 }
252
253 fn lang_tag(&self) -> &'static str {
254 "wgsl"
255 }
256
257 fn validate_ir(
258 &self,
259 repr: &Option<Self::Representation>,
260 props: &DeviceProperties,
261 ) -> Result<(), LaunchError> {
262 let shared_bytes = repr.as_ref().map(|repr| repr.shared_memory_size);
263 check_shared_memory(shared_bytes, props)
264 }
265
266 fn normalize_repr(
267 &self,
268 repr: Option<Self::Representation>,
269 ) -> (CompilerInfo, Option<AutoRepresentation>) {
270 (CompilerInfo::WGSL, repr.map(|r| r.into()))
271 }
272}
273
274#[cfg(feature = "msl")]
275impl WgpuCompiler for MslCompiler {
276 fn init(_backend: wgpu::Backend, _options: &WgpuCompilationOptions) -> Self {
277 Self::default()
278 }
279
280 fn compile_kernel(
281 &mut self,
282 _server: &mut WgpuServer<Self>,
283 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
284 definition: KernelDefinition,
285 ) -> Result<CompiledKernel<Self>, CompilationError> {
286 let compilation_options = cubecl_cpp::shared::CompilationOptions::default();
288 kernel.compile(definition, self, &compilation_options)
289 }
290
291 fn lang_tag(&self) -> &'static str {
292 "msl"
293 }
294
295 fn validate_ir(
296 &self,
297 repr: &Option<Self::Representation>,
298 props: &DeviceProperties,
299 ) -> Result<(), LaunchError> {
300 let shared_bytes = repr.as_ref().map(|repr| repr.shared_memory_size);
301 check_shared_memory(shared_bytes, props)
302 }
303
304 fn normalize_repr(
305 &self,
306 repr: Option<Self::Representation>,
307 ) -> (CompilerInfo, Option<AutoRepresentation>) {
308 (CompilerInfo::Metal, repr.map(|r| r.into()))
309 }
310}
311
312#[cfg(feature = "spirv")]
313impl WgpuCompiler for cubecl_spirv::SpirvCompiler {
314 fn init(_backend: wgpu::Backend, _options: &WgpuCompilationOptions) -> Self {
315 Self
316 }
317
318 fn compile_kernel(
319 &mut self,
320 server: &mut WgpuServer<Self>,
321 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
322 definition: KernelDefinition,
323 ) -> Result<CompiledKernel<Self>, CompilationError> {
324 crate::vulkan::compile(self, server, kernel, definition)
325 }
326
327 fn lang_tag(&self) -> &'static str {
328 "spirv"
329 }
330
331 fn validate_ir(
332 &self,
333 repr: &Option<Self::Representation>,
334 props: &DeviceProperties,
335 ) -> Result<(), LaunchError> {
336 let shared_bytes = repr.as_ref().map(|repr| repr.shared_size);
337 check_shared_memory(shared_bytes, props)
338 }
339
340 fn normalize_repr(
341 &self,
342 repr: Option<Self::Representation>,
343 ) -> (CompilerInfo, Option<AutoRepresentation>) {
344 let params_transfer = match repr.as_ref().and_then(|r| r.immediate_size) {
345 Some(_) => ParamsTransfer::Immediate,
346 None => ParamsTransfer::Uniform,
347 };
348 (
349 CompilerInfo::Vulkan { params_transfer },
350 repr.map(|r| r.into()),
351 )
352 }
353}
354
355fn check_shared_memory(
356 shared_bytes: Option<usize>,
357 props: &DeviceProperties,
358) -> Result<(), LaunchError> {
359 let max_smem = props.hardware.max_shared_memory_size;
360 if let Some(shared_bytes) = shared_bytes
361 && shared_bytes > max_smem
362 {
363 return Err(ResourceLimitError::SharedMemory {
364 requested: shared_bytes,
365 max: max_smem,
366 backtrace: BackTrace::capture(),
367 }
368 .into());
369 }
370 Ok(())
371}
372
373pub trait WgpuCompiler: Compiler {
382 fn init(backend: wgpu::Backend, options: &WgpuCompilationOptions) -> Self;
388
389 fn validate_ir(
394 &self,
395 repr: &Option<Self::Representation>,
396 props: &DeviceProperties,
397 ) -> Result<(), LaunchError>;
398
399 fn compile_kernel(
405 &mut self,
406 server: &mut WgpuServer<Self>,
407 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
408 definition: KernelDefinition,
409 ) -> Result<CompiledKernel<Self>, CompilationError>;
410
411 fn lang_tag(&self) -> &'static str;
415
416 fn normalize_repr(
422 &self,
423 repr: Option<Self::Representation>,
424 ) -> (CompilerInfo, Option<AutoRepresentation>);
425}