Skip to main content

cubecl_runtime/
kernel.rs

1use alloc::{
2    boxed::Box,
3    string::{String, ToString},
4};
5use core::{
6    fmt::Display,
7    hash::Hash,
8    marker::PhantomData,
9    sync::atomic::{AtomicI8, Ordering},
10};
11
12use cubecl_common::format::format_str;
13use cubecl_ir::{
14    ElemType, Scope,
15    metadata::Info,
16    pliron::{format, value::Value},
17    settings::KernelSettings,
18};
19use serde::{Deserialize, Serialize};
20
21use crate::{
22    compiler::{CompilationError, Compiler, CubeTask},
23    config::{CubeClRuntimeConfig, RuntimeConfig, compilation::CompilationLogLevel},
24    id::KernelId,
25    server::CubeDim,
26};
27
28/// Implement this trait to create a [kernel definition](KernelDefinition).
29pub trait KernelMetadata: Send + Sync + 'static {
30    /// Name of the kernel for debugging.
31    fn name(&self) -> &'static str {
32        core::any::type_name::<Self>()
33    }
34
35    /// Identifier for the kernel, used for caching kernel compilation.
36    fn id(&self) -> KernelId;
37
38    /// Type of addresses in this kernel
39    fn address_type(&self) -> ElemType;
40}
41
42#[allow(missing_docs)]
43pub struct KernelDefinition {
44    pub body: Scope,
45    pub info: Info,
46    pub settings: KernelSettings,
47}
48
49#[derive(Debug, PartialEq, Eq, Hash, Clone)]
50/// Global argument of a kernel.
51pub struct KernelArg {
52    /// The index of the arg.
53    pub id: usize,
54    /// The value the argument is bound to.
55    pub value: Value,
56    /// Whether the argument has metadata.
57    pub has_extended_meta: bool,
58}
59
60#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
61#[allow(missing_docs)]
62pub struct ScalarKernelArg {
63    pub ty: ElemType,
64    pub count: usize,
65}
66
67#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Hash)]
68#[allow(missing_docs)]
69#[format]
70pub enum Visibility {
71    Uniform,
72    Read,
73    ReadWrite,
74}
75
76/// A kernel, compiled in the target language
77pub struct CompiledKernel<C: Compiler> {
78    /// The name of the kernel entrypoint.
79    /// For example
80    ///
81    /// ```text
82    /// #[cube(launch)]
83    /// fn gelu_array<F: Float, R: Runtime>() {}
84    /// ```
85    ///
86    /// would have the entrypoint name "`gelu_array`".
87    pub entrypoint_name: String,
88
89    /// A fully qualified debug name of the kernel.
90    ///
91    /// For example
92    ///
93    /// ```text
94    /// #[cube(launch)]
95    /// fn gelu_array<F: Float, R: Runtime>() {}
96    /// ```
97    ///
98    /// would have a debug name such as
99    ///
100    /// ```text
101    /// gelu::gelu_array::GeluArray<
102    ///    cubecl_core::frontend::element::float::F32,
103    ///    cubecl_cuda::runtime::CudaRuntime,
104    /// >
105    /// ```
106    pub debug_name: Option<&'static str>,
107
108    /// Source code of the kernel
109    pub source: String,
110    /// In-memory representation of the kernel
111    pub repr: Option<C::Representation>,
112    /// Size of a cube for the compiled kernel
113    pub cube_dim: CubeDim,
114    /// Extra debugging information about the compiled kernel.
115    pub debug_info: Option<DebugInformation>,
116}
117
118/// Extra debugging information about the compiled kernel.
119#[derive(new)]
120pub struct DebugInformation {
121    /// The language tag of the source..
122    pub lang_tag: &'static str,
123    /// The compilation id.
124    pub id: KernelId,
125}
126
127/// Kernel that can be defined
128pub trait CubeKernel: KernelMetadata {
129    /// Define the kernel for compilation
130    fn define(&self) -> KernelDefinition;
131}
132
133/// Wraps a [`CubeKernel`] to allow it be compiled.
134pub struct KernelTask<C: Compiler, K: CubeKernel> {
135    kernel_definition: K,
136    _compiler: PhantomData<C>,
137}
138
139/// Generic [`CubeTask`] for compiling kernels
140pub struct CubeTaskKernel<C: Compiler> {
141    /// The inner compilation task being wrapped
142    pub task: Box<dyn CubeTask<C>>,
143}
144
145impl<C: Compiler, K: CubeKernel> KernelTask<C, K> {
146    /// Create a new kernel task
147    pub fn new(kernel_definition: K) -> Self {
148        Self {
149            kernel_definition,
150            _compiler: PhantomData,
151        }
152    }
153}
154
155impl<C: Compiler, K: CubeKernel> CubeTask<C> for KernelTask<C, K> {
156    fn define(&self) -> KernelDefinition {
157        self.kernel_definition.define()
158    }
159
160    fn compile(
161        &self,
162        gpu_ir: KernelDefinition,
163        compiler: &mut C,
164        compilation_options: &C::CompilationOptions,
165    ) -> Result<CompiledKernel<C>, CompilationError> {
166        let entrypoint_name = gpu_ir.settings.kernel_name.clone();
167        let cube_dim = gpu_ir.settings.cube_dim.into();
168        let lower_level_ir = compiler.compile(gpu_ir, compilation_options)?;
169
170        Ok(CompiledKernel {
171            entrypoint_name,
172            debug_name: Some(core::any::type_name::<K>()),
173            source: lower_level_ir.to_string(),
174            repr: Some(lower_level_ir),
175            cube_dim,
176            debug_info: None,
177        })
178    }
179}
180
181impl<C: Compiler, K: CubeKernel> KernelMetadata for KernelTask<C, K> {
182    // Forward ID to underlying kernel definition.
183    fn id(&self) -> KernelId {
184        self.kernel_definition.id()
185    }
186
187    // Forward name to underlying kernel definition.
188    fn name(&self) -> &'static str {
189        self.kernel_definition.name()
190    }
191
192    fn address_type(&self) -> ElemType {
193        self.kernel_definition.address_type()
194    }
195}
196
197impl<C: Compiler> KernelMetadata for Box<dyn CubeTask<C>> {
198    // Deref and use existing ID.
199    fn id(&self) -> KernelId {
200        self.as_ref().id()
201    }
202
203    // Deref and use existing name.
204    fn name(&self) -> &'static str {
205        self.as_ref().name()
206    }
207
208    fn address_type(&self) -> ElemType {
209        self.as_ref().address_type()
210    }
211}
212
213static COMPILATION_LEVEL: AtomicI8 = AtomicI8::new(-1);
214
215fn compilation_level() -> u8 {
216    let compilation_level = COMPILATION_LEVEL.load(Ordering::Relaxed);
217    if compilation_level == -1 {
218        let val = match CubeClRuntimeConfig::get().compilation.logger.level {
219            CompilationLogLevel::Full => 2,
220            CompilationLogLevel::Disabled => 0,
221            CompilationLogLevel::Basic => 1,
222        };
223
224        COMPILATION_LEVEL.store(val, Ordering::Relaxed);
225        val as u8
226    } else {
227        compilation_level as u8
228    }
229}
230
231impl<C: Compiler> Display for CompiledKernel<C> {
232    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
233        match compilation_level() {
234            2 => self.format_full(f),
235            _ => self.format_basic(f),
236        }
237    }
238}
239
240impl<C: Compiler> CompiledKernel<C> {
241    fn format_basic(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
242        f.write_str("[Compiling kernel]")?;
243        if let Some(name) = self.debug_name {
244            if name.len() <= 32 {
245                f.write_fmt(format_args!(" {name}"))?;
246            } else {
247                f.write_fmt(format_args!(" {}", name.split('<').next().unwrap_or("")))?;
248            }
249        }
250
251        Ok(())
252    }
253
254    fn format_full(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
255        f.write_str("[START_KERNEL_COMPILATION]")?;
256
257        if let Some(name) = self.debug_name {
258            if name.len() <= 32 {
259                f.write_fmt(format_args!("\nname: {name}"))?;
260            } else {
261                let name = format_str(name, &[('<', '>')], false);
262                f.write_fmt(format_args!("\nname: {name}"))?;
263            }
264        }
265
266        if let Some(info) = &self.debug_info {
267            f.write_fmt(format_args!("\nid: {:#?}", info.id))?;
268        }
269
270        f.write_fmt(format_args!(
271            "
272source:
273```{}
274{}
275```
276[END_KERNEL_COMPILATION]
277",
278            self.debug_info
279                .as_ref()
280                .map(|info| info.lang_tag)
281                .unwrap_or(""),
282            self.source
283        ))
284    }
285}