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