1use std::fmt::Display;
2
3use cubecl_core::{
4 Compiler, ExecutionMode, WgpuCompilationOptions,
5 ir::StorageType,
6 prelude::{CompiledKernel, KernelDefinition},
7 server::{ComputeServer, LaunchError, ResourceLimitError},
8};
9#[cfg(feature = "msl")]
10use cubecl_cpp::shared::MslComputeKernel;
11use cubecl_environment::backtrace::BackTrace;
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 definition: KernelDefinition,
196 mode: ExecutionMode,
197 ) -> Result<CompiledKernel<Self>, CompilationError> {
198 match self {
199 AutoCompiler::Wgsl(_) => kernel.compile(
200 definition,
201 self,
202 &server.compilation_options,
203 mode,
204 kernel.address_type(),
205 ),
206 #[cfg(feature = "spirv")]
207 AutoCompiler::SpirV(_) => {
208 #[cfg(feature = "spirv-dump")]
209 let (name, id) = (kernel.name().to_string(), kernel.id());
210 let compiled = crate::vulkan::compile(self, server, kernel, definition, mode)?;
211 #[cfg(feature = "spirv-dump")]
212 if let Some(spirv) = compiled.repr.as_ref().and_then(|r| r.as_spirv()) {
213 crate::vulkan::dump_spirv(spirv, &name, id);
214 }
215 Ok(compiled)
216 }
217 #[cfg(feature = "msl")]
218 AutoCompiler::Msl(_) => kernel.compile(
219 definition,
220 self,
221 &server.compilation_options,
222 mode,
223 kernel.address_type(),
224 ),
225 }
226 }
227
228 fn lang_tag(&self) -> &'static str {
229 match self {
230 AutoCompiler::Wgsl(_) => "wgsl",
231 #[cfg(feature = "spirv")]
232 AutoCompiler::SpirV(_) => "spirv",
233 #[cfg(feature = "msl")]
234 AutoCompiler::Msl(_) => "msl",
235 }
236 }
237
238 fn validate_ir(
239 &self,
240 repr: &Option<Self::Representation>,
241 props: &DeviceProperties,
242 ) -> Result<(), LaunchError> {
243 let shared_bytes = repr.as_ref().map(|repr| match repr {
244 AutoRepresentation::Wgsl(repr) => repr.shared_memory_bytes(),
245 #[cfg(feature = "msl")]
246 AutoRepresentation::Msl(repr) => repr.shared_memory_size(),
247 #[cfg(feature = "spirv")]
248 AutoRepresentation::SpirV(repr) => repr.shared_size,
249 });
250 check_shared_memory(shared_bytes, props)
251 }
252
253 fn normalize_repr(
254 &self,
255 repr: Option<Self::Representation>,
256 ) -> (CompilerInfo, Option<AutoRepresentation>) {
257 let compiler_info = match &repr {
258 #[cfg(feature = "spirv")]
259 Some(AutoRepresentation::SpirV(repr)) => CompilerInfo::Vulkan {
260 params_transfer: match repr.immediate_size {
261 Some(_) => ParamsTransfer::Immediate,
262 None => ParamsTransfer::Uniform,
263 },
264 },
265 #[cfg(feature = "msl")]
266 Some(AutoRepresentation::Msl(_)) => CompilerInfo::Metal,
267 Some(AutoRepresentation::Wgsl(_)) => CompilerInfo::WGSL,
268 None => CompilerInfo::None,
269 };
270
271 (compiler_info, repr)
272 }
273}
274
275impl WgpuCompiler for WgslCompiler {
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 mode: ExecutionMode,
286 ) -> Result<CompiledKernel<Self>, CompilationError> {
287 kernel.compile(
288 definition,
289 self,
290 &server.compilation_options,
291 mode,
292 kernel.address_type(),
293 )
294 }
295
296 fn lang_tag(&self) -> &'static str {
297 "wgsl"
298 }
299
300 fn validate_ir(
301 &self,
302 repr: &Option<Self::Representation>,
303 props: &DeviceProperties,
304 ) -> Result<(), LaunchError> {
305 let shared_bytes = repr.as_ref().map(|repr| repr.shared_memory_bytes());
306 check_shared_memory(shared_bytes, props)
307 }
308
309 fn normalize_repr(
310 &self,
311 repr: Option<Self::Representation>,
312 ) -> (CompilerInfo, Option<AutoRepresentation>) {
313 (CompilerInfo::WGSL, repr.map(|r| r.into()))
314 }
315}
316
317#[cfg(feature = "msl")]
318impl WgpuCompiler for MslCompiler {
319 fn init(_backend: wgpu::Backend, _options: &WgpuCompilationOptions) -> Self {
320 Self::default()
321 }
322
323 fn compile_kernel(
324 &mut self,
325 _server: &mut WgpuServer<Self>,
326 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
327 definition: KernelDefinition,
328 mode: ExecutionMode,
329 ) -> Result<CompiledKernel<Self>, CompilationError> {
330 let compilation_options = cubecl_cpp::shared::CompilationOptions::default();
332 kernel.compile(
333 definition,
334 self,
335 &compilation_options,
336 mode,
337 kernel.address_type(),
338 )
339 }
340
341 fn lang_tag(&self) -> &'static str {
342 "msl"
343 }
344
345 fn validate_ir(
346 &self,
347 repr: &Option<Self::Representation>,
348 props: &DeviceProperties,
349 ) -> Result<(), LaunchError> {
350 let shared_bytes = repr.as_ref().map(|repr| repr.shared_memory_size());
351 check_shared_memory(shared_bytes, props)
352 }
353
354 fn normalize_repr(
355 &self,
356 repr: Option<Self::Representation>,
357 ) -> (CompilerInfo, Option<AutoRepresentation>) {
358 (CompilerInfo::Metal, repr.map(|r| r.into()))
359 }
360}
361
362#[cfg(feature = "spirv")]
363impl<T: cubecl_spirv::SpirvTarget> WgpuCompiler for cubecl_spirv::SpirvCompiler<T> {
364 fn init(_backend: wgpu::Backend, _options: &WgpuCompilationOptions) -> Self {
365 Self::default()
366 }
367
368 fn compile_kernel(
369 &mut self,
370 server: &mut WgpuServer<Self>,
371 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
372 definition: KernelDefinition,
373 mode: ExecutionMode,
374 ) -> Result<CompiledKernel<Self>, CompilationError> {
375 #[cfg(feature = "spirv-dump")]
376 let (name, id) = (kernel.name().to_string(), kernel.id());
377 let compiled = crate::vulkan::compile(self, server, kernel, definition, mode)?;
378 #[cfg(feature = "spirv-dump")]
379 if let Some(spirv) = compiled.repr.as_ref() {
380 crate::vulkan::dump_spirv(spirv, &name, id);
381 }
382 Ok(compiled)
383 }
384
385 fn lang_tag(&self) -> &'static str {
386 "spirv"
387 }
388
389 fn validate_ir(
390 &self,
391 repr: &Option<Self::Representation>,
392 props: &DeviceProperties,
393 ) -> Result<(), LaunchError> {
394 let shared_bytes = repr.as_ref().map(|repr| repr.shared_size);
395 check_shared_memory(shared_bytes, props)
396 }
397
398 fn normalize_repr(
399 &self,
400 repr: Option<Self::Representation>,
401 ) -> (CompilerInfo, Option<AutoRepresentation>) {
402 let params_transfer = match repr.as_ref().and_then(|r| r.immediate_size) {
403 Some(_) => ParamsTransfer::Immediate,
404 None => ParamsTransfer::Uniform,
405 };
406 (
407 CompilerInfo::Vulkan { params_transfer },
408 repr.map(|r| r.into()),
409 )
410 }
411}
412
413fn check_shared_memory(
414 shared_bytes: Option<usize>,
415 props: &DeviceProperties,
416) -> Result<(), LaunchError> {
417 let max_smem = props.hardware.max_shared_memory_size;
418 if let Some(shared_bytes) = shared_bytes
419 && shared_bytes > max_smem
420 {
421 return Err(ResourceLimitError::SharedMemory {
422 requested: shared_bytes,
423 max: max_smem,
424 backtrace: BackTrace::capture(),
425 }
426 .into());
427 }
428 Ok(())
429}
430
431pub trait WgpuCompiler: Compiler {
440 fn init(backend: wgpu::Backend, options: &WgpuCompilationOptions) -> Self;
446
447 fn validate_ir(
452 &self,
453 repr: &Option<Self::Representation>,
454 props: &DeviceProperties,
455 ) -> Result<(), LaunchError>;
456
457 fn compile_kernel(
463 &mut self,
464 server: &mut WgpuServer<Self>,
465 kernel: <WgpuServer<Self> as ComputeServer>::Kernel,
466 definition: KernelDefinition,
467 mode: ExecutionMode,
468 ) -> Result<CompiledKernel<Self>, CompilationError>;
469
470 fn lang_tag(&self) -> &'static str;
474
475 fn normalize_repr(
481 &self,
482 repr: Option<Self::Representation>,
483 ) -> (CompilerInfo, Option<AutoRepresentation>);
484}