Skip to main content

cubecl_server/
kernel.rs

1//! The compile step of a launch: what a [`CubeKernel`] becomes once a
2//! [`Compiler`] has seen it.
3
4pub use cubecl_runtime::kernel::*;
5
6use alloc::string::{String, ToString};
7use core::{
8    fmt::Display,
9    sync::atomic::{AtomicI8, Ordering},
10};
11
12use cubecl_common::format::format_str;
13use cubecl_environment::backtrace::BackTrace;
14
15use crate::{
16    compiler::{CompilationError, Compiler},
17    config::{CubeClRuntimeConfig, RuntimeConfig, compilation::CompilationLogLevel},
18    id::KernelId,
19    server::CubeDim,
20};
21
22/// A kernel, compiled in the target language
23pub struct CompiledKernel<C: Compiler> {
24    /// The name of the kernel entrypoint.
25    /// For example
26    ///
27    /// ```text
28    /// #[cube(launch)]
29    /// fn gelu_array<F: Float>() {}
30    /// ```
31    ///
32    /// would have the entrypoint name "`gelu_array`".
33    pub entrypoint_name: String,
34
35    /// A fully qualified debug name of the kernel.
36    ///
37    /// For example
38    ///
39    /// ```text
40    /// #[cube(launch)]
41    /// fn gelu_array<F: Float>() {}
42    /// ```
43    ///
44    /// would have a debug name such as
45    ///
46    /// ```text
47    /// gelu::gelu_array::GeluArray<
48    ///    cubecl_core::frontend::element::float::F32,
49    ///    cubecl_cuda::runtime::CudaRuntime,
50    /// >
51    /// ```
52    pub debug_name: Option<&'static str>,
53
54    /// Source code of the kernel
55    pub source: String,
56    /// In-memory representation of the kernel
57    pub repr: Option<C::Representation>,
58    /// Size of a cube for the compiled kernel
59    pub cube_dim: CubeDim,
60    /// What the kernel does with each buffer binding, by buffer position —
61    /// see [`BufferIOAttr`]. `None` when the compiler kept no answer, which the
62    /// launch path reads as every buffer both read and written: the
63    /// conservative direction, since over-claiming costs a spurious loud
64    /// failure and under-claiming costs a silent clean read of garbage.
65    pub io: Option<alloc::vec::Vec<BufferIOAttr>>,
66    /// Extra debugging information about the compiled kernel.
67    pub debug_info: Option<DebugInformation>,
68}
69
70/// Extra debugging information about the compiled kernel.
71#[derive(new)]
72pub struct DebugInformation {
73    /// The language tag of the source..
74    pub lang_tag: &'static str,
75    /// The compilation id.
76    pub id: KernelId,
77}
78
79impl<C: Compiler> CompiledKernel<C> {
80    /// Compile `definition` with `compiler`, keeping `kernel`'s name as the
81    /// debug name of the result.
82    pub fn compile(
83        kernel: &dyn CubeKernel,
84        definition: KernelDefinition,
85        compiler: &mut C,
86        compilation_options: &C::CompilationOptions,
87    ) -> Result<Self, CompilationError> {
88        let entrypoint_name = definition.settings.kernel_name.clone();
89        let cube_dim = definition.settings.cube_dim.into();
90
91        // A hand-written kernel is already in the target language: there is no
92        // IR to hand the compiler, so neither analysis it produces exists.
93        // `io: None` reads as every buffer both read and written, which is the
94        // conservative direction.
95        if let Some(precompiled) = kernel.source() {
96            if precompiled.lang != compiler.lang_tag() {
97                return Err(CompilationError::Generic {
98                    reason: alloc::format!(
99                        "kernel `{}` carries {} source, but this compiler expects {}",
100                        kernel.name(),
101                        precompiled.lang,
102                        compiler.lang_tag()
103                    ),
104                    backtrace: BackTrace::capture(),
105                });
106            }
107            return Ok(CompiledKernel {
108                entrypoint_name: precompiled.entrypoint_name,
109                debug_name: Some(kernel.name()),
110                source: precompiled.source,
111                io: None,
112                repr: None,
113                cube_dim,
114                debug_info: None,
115            });
116        }
117
118        let lower_level_ir = compiler.compile(definition, compilation_options)?;
119
120        Ok(CompiledKernel {
121            entrypoint_name,
122            debug_name: Some(kernel.name()),
123            source: lower_level_ir.to_string(),
124            io: C::buffer_io(&lower_level_ir),
125            repr: Some(lower_level_ir),
126            cube_dim,
127            debug_info: None,
128        })
129    }
130}
131
132static COMPILATION_LEVEL: AtomicI8 = AtomicI8::new(-1);
133
134fn compilation_level() -> u8 {
135    let compilation_level = COMPILATION_LEVEL.load(Ordering::Relaxed);
136    if compilation_level == -1 {
137        let val = match CubeClRuntimeConfig::get().compilation.logger.level {
138            CompilationLogLevel::Full => 2,
139            CompilationLogLevel::Disabled => 0,
140            CompilationLogLevel::Basic => 1,
141        };
142
143        COMPILATION_LEVEL.store(val, Ordering::Relaxed);
144        val as u8
145    } else {
146        compilation_level as u8
147    }
148}
149
150impl<C: Compiler> Display for CompiledKernel<C> {
151    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
152        match compilation_level() {
153            2 => self.format_full(f),
154            _ => self.format_basic(f),
155        }
156    }
157}
158
159impl<C: Compiler> CompiledKernel<C> {
160    fn format_basic(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
161        f.write_str("[Compiling kernel]")?;
162        if let Some(name) = self.debug_name {
163            if name.len() <= 32 {
164                f.write_fmt(format_args!(" {name}"))?;
165            } else {
166                f.write_fmt(format_args!(" {}", name.split('<').next().unwrap_or("")))?;
167            }
168        }
169
170        Ok(())
171    }
172
173    fn format_full(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174        f.write_str("[START_KERNEL_COMPILATION]")?;
175
176        if let Some(name) = self.debug_name {
177            if name.len() <= 32 {
178                f.write_fmt(format_args!("\nname: {name}"))?;
179            } else {
180                let name = format_str(name, &[('<', '>')], false);
181                f.write_fmt(format_args!("\nname: {name}"))?;
182            }
183        }
184
185        if let Some(info) = &self.debug_info {
186            f.write_fmt(format_args!("\nid: {:#?}", info.id))?;
187        }
188
189        f.write_fmt(format_args!(
190            "
191source:
192```{}
193{}
194```
195[END_KERNEL_COMPILATION]
196",
197            self.debug_info
198                .as_ref()
199                .map(|info| info.lang_tag)
200                .unwrap_or(""),
201            self.source
202        ))
203    }
204}