1use alloc::{
2 boxed::Box,
3 string::{String, ToString},
4 vec::Vec,
5};
6use core::{
7 fmt::Display,
8 marker::PhantomData,
9 sync::atomic::{AtomicI8, Ordering},
10};
11
12use cubecl_common::format::format_str;
13use cubecl_ir::{Id, Scope, StorageType, Value};
14use serde::{Deserialize, Serialize};
15
16use crate::{
17 compiler::{CompilationError, Compiler, CubeTask},
18 config::{CubeClRuntimeConfig, RuntimeConfig, compilation::CompilationLogLevel},
19 id::KernelId,
20 server::{CubeDim, ExecutionMode},
21};
22
23pub trait KernelMetadata: Send + Sync + 'static {
25 fn name(&self) -> &'static str {
27 core::any::type_name::<Self>()
28 }
29
30 fn id(&self) -> KernelId;
32
33 fn address_type(&self) -> StorageType;
35}
36
37#[derive(Debug, Clone)]
38#[allow(missing_docs)]
39pub struct KernelDefinition {
40 pub buffers: Vec<KernelArg>,
41 pub tensor_maps: Vec<KernelArg>,
42 pub scalars: Vec<ScalarKernelArg>,
43 pub cube_dim: CubeDim,
44 pub body: Scope,
45 pub options: KernelOptions,
46}
47
48impl KernelDefinition {
49 pub fn num_global_buffers(&self) -> usize {
51 self.buffers.len() + self.tensor_maps.len()
52 }
53}
54
55#[derive(Default, Clone, Debug, Hash, PartialEq, Eq)]
56pub struct KernelOptions {
58 pub kernel_name: String,
60 pub debug_symbols: bool,
62 pub cluster_dim: Option<CubeDim>,
64}
65
66#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
67pub struct KernelArg {
69 pub id: Id,
71 pub value: Value,
73 pub has_extended_meta: bool,
75}
76
77#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
78#[allow(missing_docs)]
79pub struct ScalarKernelArg {
80 pub ty: StorageType,
81 pub count: usize,
82}
83
84#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash)]
85#[allow(missing_docs)]
86pub enum Visibility {
87 Uniform,
88 Read,
89 ReadWrite,
90}
91
92pub struct CompiledKernel<C: Compiler> {
94 pub entrypoint_name: String,
104
105 pub debug_name: Option<&'static str>,
123
124 pub source: String,
126 pub repr: Option<C::Representation>,
128 pub cube_dim: CubeDim,
130 pub debug_info: Option<DebugInformation>,
132}
133
134#[derive(new)]
136pub struct DebugInformation {
137 pub lang_tag: &'static str,
139 pub id: KernelId,
141}
142
143pub trait CubeKernel: KernelMetadata {
145 fn define(&self) -> KernelDefinition;
147}
148
149pub struct KernelTask<C: Compiler, K: CubeKernel> {
151 kernel_definition: K,
152 _compiler: PhantomData<C>,
153}
154
155pub struct CubeTaskKernel<C: Compiler> {
157 pub task: Box<dyn CubeTask<C>>,
159}
160
161impl<C: Compiler, K: CubeKernel> KernelTask<C, K> {
162 pub fn new(kernel_definition: K) -> Self {
164 Self {
165 kernel_definition,
166 _compiler: PhantomData,
167 }
168 }
169}
170
171impl<C: Compiler, K: CubeKernel> CubeTask<C> for KernelTask<C, K> {
172 fn compile(
173 &self,
174 compiler: &mut C,
175 compilation_options: &C::CompilationOptions,
176 mode: ExecutionMode,
177 addr_type: StorageType,
178 ) -> Result<CompiledKernel<C>, CompilationError> {
179 let gpu_ir = self.kernel_definition.define();
180 let entrypoint_name = gpu_ir.options.kernel_name.clone();
181 let cube_dim = gpu_ir.cube_dim;
182 let lower_level_ir = compiler.compile(gpu_ir, compilation_options, mode, addr_type)?;
183
184 Ok(CompiledKernel {
185 entrypoint_name,
186 debug_name: Some(core::any::type_name::<K>()),
187 source: lower_level_ir.to_string(),
188 repr: Some(lower_level_ir),
189 cube_dim,
190 debug_info: None,
191 })
192 }
193}
194
195impl<C: Compiler, K: CubeKernel> KernelMetadata for KernelTask<C, K> {
196 fn id(&self) -> KernelId {
198 self.kernel_definition.id()
199 }
200
201 fn name(&self) -> &'static str {
203 self.kernel_definition.name()
204 }
205
206 fn address_type(&self) -> StorageType {
207 self.kernel_definition.address_type()
208 }
209}
210
211impl<C: Compiler> KernelMetadata for Box<dyn CubeTask<C>> {
212 fn id(&self) -> KernelId {
214 self.as_ref().id()
215 }
216
217 fn name(&self) -> &'static str {
219 self.as_ref().name()
220 }
221
222 fn address_type(&self) -> StorageType {
223 self.as_ref().address_type()
224 }
225}
226
227static COMPILATION_LEVEL: AtomicI8 = AtomicI8::new(-1);
228
229fn compilation_level() -> u8 {
230 let compilation_level = COMPILATION_LEVEL.load(Ordering::Relaxed);
231 if compilation_level == -1 {
232 let val = match CubeClRuntimeConfig::get().compilation.logger.level {
233 CompilationLogLevel::Full => 2,
234 CompilationLogLevel::Disabled => 0,
235 CompilationLogLevel::Basic => 1,
236 };
237
238 COMPILATION_LEVEL.store(val, Ordering::Relaxed);
239 val as u8
240 } else {
241 compilation_level as u8
242 }
243}
244
245impl<C: Compiler> Display for CompiledKernel<C> {
246 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
247 match compilation_level() {
248 2 => self.format_full(f),
249 _ => self.format_basic(f),
250 }
251 }
252}
253
254impl<C: Compiler> CompiledKernel<C> {
255 fn format_basic(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
256 f.write_str("[Compiling kernel]")?;
257 if let Some(name) = self.debug_name {
258 if name.len() <= 32 {
259 f.write_fmt(format_args!(" {name}"))?;
260 } else {
261 f.write_fmt(format_args!(" {}", name.split('<').next().unwrap_or("")))?;
262 }
263 }
264
265 Ok(())
266 }
267
268 fn format_full(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
269 f.write_str("[START_KERNEL_COMPILATION]")?;
270
271 if let Some(name) = self.debug_name {
272 if name.len() <= 32 {
273 f.write_fmt(format_args!("\nname: {name}"))?;
274 } else {
275 let name = format_str(name, &[('<', '>')], false);
276 f.write_fmt(format_args!("\nname: {name}"))?;
277 }
278 }
279
280 if let Some(info) = &self.debug_info {
281 f.write_fmt(format_args!("\nid: {:#?}", info.id))?;
282 }
283
284 f.write_fmt(format_args!(
285 "
286source:
287```{}
288{}
289```
290[END_KERNEL_COMPILATION]
291",
292 self.debug_info
293 .as_ref()
294 .map(|info| info.lang_tag)
295 .unwrap_or(""),
296 self.source
297 ))
298 }
299}