1use std::fmt::Display;
2
3use cubecl_core::{
4 Compiler, ExecutionMode, WgpuCompilationOptions,
5 backtrace::BackTrace,
6 ir::StorageType,
7 prelude::{CompiledKernel, KernelDefinition},
8 server::{ComputeServer, LaunchError, ResourceLimitError},
9};
10#[cfg(feature = "msl")]
11use cubecl_cpp::shared::MslComputeKernel;
12use cubecl_ir::DeviceProperties;
13use cubecl_runtime::compiler::CompilationError;
14use derive_more::derive::From;
15
16#[cfg(feature = "spirv")]
17use crate::ParamsTransfer;
18use crate::{CompilerInfo, WgpuServer};
19
20use super::wgsl;
21
22#[cfg(feature = "msl")]
23pub use cubecl_cpp::MslCompiler;
24#[cfg(feature = "spirv")]
25pub use cubecl_spirv::SpirvCompiler;
26pub use wgsl::WgslCompiler;
27
28#[allow(clippy::large_enum_variant)]
34#[derive(Debug, Clone)]
35pub enum AutoCompiler {
36 Wgsl(WgslCompiler),
38 #[cfg(feature = "spirv")]
40 SpirV(cubecl_spirv::SpirvCompiler),
41 #[cfg(feature = "msl")]
43 Msl(MslCompiler),
44}
45
46#[derive(From)]
48#[allow(clippy::large_enum_variant)]
49pub enum AutoRepresentation {
50 Wgsl(wgsl::ComputeShader),
52 #[cfg(feature = "spirv")]
54 SpirV(cubecl_spirv::SpirvKernel),
55 #[cfg(feature = "msl")]
57 Msl(MslComputeKernel),
58}
59
60#[derive(From, Clone, Copy)]
62#[allow(clippy::large_enum_variant)]
63pub enum AutoRepresentationRef<'a> {
64 Wgsl(&'a wgsl::ComputeShader),
66 #[cfg(feature = "spirv")]
68 SpirV(&'a cubecl_spirv::SpirvKernel),
69 #[cfg(feature = "msl")]
71 Msl(&'a MslComputeKernel),
72}
73
74#[cfg(feature = "spirv")]
75impl AutoRepresentation {
76 pub fn as_spirv(&self) -> Option<&cubecl_spirv::SpirvKernel> {
78 match self {
79 AutoRepresentation::SpirV(repr) => Some(repr),
80 _ => None,
81 }
82 }
83}
84
85#[cfg(feature = "msl")]
86impl AutoRepresentation {
87 pub fn as_msl(&self) -> Option<&MslComputeKernel> {
89 match self {
90 AutoRepresentation::Msl(repr) => Some(repr),
91 _ => None,
92 }
93 }
94}
95
96impl AutoRepresentation {
97 pub fn as_ref(&self) -> AutoRepresentationRef<'_> {
99 match self {
100 AutoRepresentation::Wgsl(compute_shader) => AutoRepresentationRef::Wgsl(compute_shader),
101 #[cfg(feature = "spirv")]
102 AutoRepresentation::SpirV(spirv_kernel) => AutoRepresentationRef::SpirV(spirv_kernel),
103 #[cfg(feature = "msl")]
104 AutoRepresentation::Msl(compute_shader) => AutoRepresentationRef::Msl(compute_shader),
105 }
106 }
107}
108
109impl Display for AutoRepresentation {
110 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::fmt::Result {
111 match self {
112 AutoRepresentation::Wgsl(compute_shader) => compute_shader.fmt(f),
113 #[cfg(feature = "spirv")]
114 AutoRepresentation::SpirV(spirv_kernel) => spirv_kernel.fmt(f),
115 #[cfg(feature = "msl")]
116 AutoRepresentation::Msl(compute_shader) => compute_shader.fmt(f),
117 }
118 }
119}
120
121impl Compiler for AutoCompiler {
122 type Representation = AutoRepresentation;
123
124 type CompilationOptions = WgpuCompilationOptions;
125
126 fn compile(
127 &mut self,
128 kernel: KernelDefinition,
129 compilation_options: &Self::CompilationOptions,
130 mode: ExecutionMode,
131 addr_type: StorageType,
132 ) -> Result<Self::Representation, CompilationError> {
133 let kernel = match self {
134 AutoCompiler::Wgsl(wgsl_compiler) => {
135 Compiler::compile(wgsl_compiler, kernel, compilation_options, mode, addr_type)?
136 .into()
137 }
138 #[cfg(feature = "spirv")]
139 AutoCompiler::SpirV(spirv_compiler) => {
140 Compiler::compile(spirv_compiler, kernel, compilation_options, mode, addr_type)?
141 .into()
142 }
143 #[cfg(feature = "msl")]
144 AutoCompiler::Msl(msl_compiler) => {
145 use cubecl_cpp;
147 let compilation_options = cubecl_cpp::shared::CompilationOptions::default();
148 Compiler::compile(msl_compiler, kernel, &compilation_options, mode, addr_type)?
149 .into()
150 }
151 };
152
153 Ok(kernel)
154 }
155
156 fn elem_size(&self, elem: cubecl_core::ir::ElemType) -> usize {
157 match self {
158 AutoCompiler::Wgsl(wgsl_compiler) => wgsl_compiler.elem_size(elem),
159 #[cfg(feature = "spirv")]
160 AutoCompiler::SpirV(spirv_compiler) => spirv_compiler.elem_size(elem),
161 #[cfg(feature = "msl")]
162 AutoCompiler::Msl(msl_compiler) => msl_compiler.elem_size(elem),
163 }
164 }
165
166 fn extension(&self) -> &'static str {
167 match self {
168 AutoCompiler::Wgsl(_) => "wgsl",
169 #[cfg(feature = "spirv")]
170 AutoCompiler::SpirV(_) => "spv",
171 #[cfg(feature = "msl")]
172 AutoCompiler::Msl(_) => "msl",
173 }
174 }
175}
176
177impl WgpuCompiler for AutoCompiler {
178 fn init(backend: wgpu::Backend, options: &WgpuCompilationOptions) -> Self {
179 let _ = options; match backend {
181 #[cfg(feature = "spirv")]
182 wgpu::Backend::Vulkan if options.supports_vulkan_compiler => {
183 AutoCompiler::SpirV(Default::default())
184 }
185 #[cfg(feature = "msl")]
186 wgpu::Backend::Metal => AutoCompiler::Msl(Default::default()),
187 _ => AutoCompiler::Wgsl(Default::default()),
188 }
189 }
190
191 fn compile_kernel(
192 &mut self,
193 server: &mut WgpuServer<AutoCompiler>,
194 kernel: <WgpuServer<AutoCompiler> as ComputeServer>::Kernel,
195 mode: ExecutionMode,
196 ) -> Result<CompiledKernel<Self>, CompilationError> {
197 match self {
198 AutoCompiler::Wgsl(_) => kernel.compile(
199 self,
200 &server.compilation_options,
201 mode,
202 kernel.address_type(),
203 ),
204 #[cfg(feature = "spirv")]
205 AutoCompiler::SpirV(_) => {
206 #[cfg(feature = "spirv-dump")]
207 let (name, id) = (kernel.name().to_string(), kernel.id());
208 let compiled = crate::vulkan::compile(self, server, kernel, mode)?;
209 #[cfg(feature = "spirv-dump")]
210 if let Some(spirv) = compiled.repr.as_ref().and_then(|r| r.as_spirv()) {
211 crate::vulkan::dump_spirv(spirv, &name, id);
212 }
213 Ok(compiled)
214 }
215 #[cfg(feature = "msl")]
216 AutoCompiler::Msl(_) => kernel.compile(
217 self,
218 &server.compilation_options,
219 mode,
220 kernel.address_type(),
221 ),
222 }
223 }
224
225 fn lang_tag(&self) -> &'static str {
226 match self {
227 AutoCompiler::Wgsl(_) => "wgsl",
228 #[cfg(feature = "spirv")]
229 AutoCompiler::SpirV(_) => "spirv",
230 #[cfg(feature = "msl")]
231 AutoCompiler::Msl(_) => "msl",
232 }
233 }
234
235 fn validate_ir(
236 &self,
237 repr: &Option<Self::Representation>,
238 props: &DeviceProperties,
239 ) -> Result<(), LaunchError> {
240 let shared_bytes = repr.as_ref().map(|repr| match repr {
241 AutoRepresentation::Wgsl(repr) => repr.shared_memory_bytes(),
242 #[cfg(feature = "msl")]
243 AutoRepresentation::Msl(repr) => repr.shared_memory_size(),
244 #[cfg(feature = "spirv")]
245 AutoRepresentation::SpirV(repr) => repr.shared_size,
246 });
247 check_shared_memory(shared_bytes, props)
248 }
249
250 fn normalize_repr(
251 &self,
252 repr: Option<Self::Representation>,
253 ) -> (CompilerInfo, Option<AutoRepresentation>) {
254 let compiler_info = match &repr {
255 #[cfg(feature = "spirv")]
256 Some(AutoRepresentation::SpirV(repr)) => CompilerInfo::Vulkan {
257 params_transfer: match repr.immediate_size {
258 Some(_) => ParamsTransfer::Immediate,
259 None => ParamsTransfer::Uniform,
260 },
261 },
262 #[cfg(feature = "msl")]
263 Some(AutoRepresentation::Msl(_)) => CompilerInfo::Metal,
264 Some(AutoRepresentation::Wgsl(_)) => CompilerInfo::WGSL,
265 None => CompilerInfo::None,
266 };
267
268 (compiler_info, repr)
269 }
270}
271
272impl WgpuCompiler for WgslCompiler {
273 fn init(_backend: wgpu::Backend, _options: &WgpuCompilationOptions) -> Self {
274 Self::default()
275 }
276
277 fn compile_kernel(
278 &mut self,
279 server: &mut WgpuServer<Self>,
280 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
281 mode: ExecutionMode,
282 ) -> Result<CompiledKernel<Self>, CompilationError> {
283 kernel.compile(
284 self,
285 &server.compilation_options,
286 mode,
287 kernel.address_type(),
288 )
289 }
290
291 fn lang_tag(&self) -> &'static str {
292 "wgsl"
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_bytes());
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::WGSL, repr.map(|r| r.into()))
309 }
310}
311
312#[cfg(feature = "msl")]
313impl WgpuCompiler for MslCompiler {
314 fn init(_backend: wgpu::Backend, _options: &WgpuCompilationOptions) -> Self {
315 Self::default()
316 }
317
318 fn compile_kernel(
319 &mut self,
320 _server: &mut WgpuServer<Self>,
321 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
322 mode: ExecutionMode,
323 ) -> Result<CompiledKernel<Self>, CompilationError> {
324 let compilation_options = cubecl_cpp::shared::CompilationOptions::default();
326 kernel.compile(self, &compilation_options, mode, kernel.address_type())
327 }
328
329 fn lang_tag(&self) -> &'static str {
330 "msl"
331 }
332
333 fn validate_ir(
334 &self,
335 repr: &Option<Self::Representation>,
336 props: &DeviceProperties,
337 ) -> Result<(), LaunchError> {
338 let shared_bytes = repr.as_ref().map(|repr| repr.shared_memory_size());
339 check_shared_memory(shared_bytes, props)
340 }
341
342 fn normalize_repr(
343 &self,
344 repr: Option<Self::Representation>,
345 ) -> (CompilerInfo, Option<AutoRepresentation>) {
346 (CompilerInfo::Metal, repr.map(|r| r.into()))
347 }
348}
349
350#[cfg(feature = "spirv")]
351impl<T: cubecl_spirv::SpirvTarget> WgpuCompiler for cubecl_spirv::SpirvCompiler<T> {
352 fn init(_backend: wgpu::Backend, _options: &WgpuCompilationOptions) -> Self {
353 Self::default()
354 }
355
356 fn compile_kernel(
357 &mut self,
358 server: &mut WgpuServer<Self>,
359 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
360 mode: ExecutionMode,
361 ) -> Result<CompiledKernel<Self>, CompilationError> {
362 #[cfg(feature = "spirv-dump")]
363 let (name, id) = (kernel.name().to_string(), kernel.id());
364 let compiled = crate::vulkan::compile(self, server, kernel, mode)?;
365 #[cfg(feature = "spirv-dump")]
366 if let Some(spirv) = compiled.repr.as_ref() {
367 crate::vulkan::dump_spirv(spirv, &name, id);
368 }
369 Ok(compiled)
370 }
371
372 fn lang_tag(&self) -> &'static str {
373 "spirv"
374 }
375
376 fn validate_ir(
377 &self,
378 repr: &Option<Self::Representation>,
379 props: &DeviceProperties,
380 ) -> Result<(), LaunchError> {
381 let shared_bytes = repr.as_ref().map(|repr| repr.shared_size);
382 check_shared_memory(shared_bytes, props)
383 }
384
385 fn normalize_repr(
386 &self,
387 repr: Option<Self::Representation>,
388 ) -> (CompilerInfo, Option<AutoRepresentation>) {
389 let params_transfer = match repr.as_ref().and_then(|r| r.immediate_size) {
390 Some(_) => ParamsTransfer::Immediate,
391 None => ParamsTransfer::Uniform,
392 };
393 (
394 CompilerInfo::Vulkan { params_transfer },
395 repr.map(|r| r.into()),
396 )
397 }
398}
399
400fn check_shared_memory(
401 shared_bytes: Option<usize>,
402 props: &DeviceProperties,
403) -> Result<(), LaunchError> {
404 let max_smem = props.hardware.max_shared_memory_size;
405 if let Some(shared_bytes) = shared_bytes
406 && shared_bytes > max_smem
407 {
408 return Err(ResourceLimitError::SharedMemory {
409 requested: shared_bytes,
410 max: max_smem,
411 backtrace: BackTrace::capture(),
412 }
413 .into());
414 }
415 Ok(())
416}
417
418pub trait WgpuCompiler: Compiler {
427 fn init(backend: wgpu::Backend, options: &WgpuCompilationOptions) -> Self;
433
434 fn validate_ir(
439 &self,
440 repr: &Option<Self::Representation>,
441 props: &DeviceProperties,
442 ) -> Result<(), LaunchError>;
443
444 fn compile_kernel(
450 &mut self,
451 server: &mut WgpuServer<Self>,
452 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
453 mode: ExecutionMode,
454 ) -> Result<CompiledKernel<Self>, CompilationError>;
455
456 fn lang_tag(&self) -> &'static str;
460
461 fn normalize_repr(
467 &self,
468 repr: Option<Self::Representation>,
469 ) -> (CompilerInfo, Option<AutoRepresentation>);
470}