use crate::{
id::KernelId,
kernel::{CompiledKernel, KernelDefinition, KernelMetadata},
server::ExecutionMode,
};
use alloc::string::String;
use core::hash::Hash;
use cubecl_common::hash::StableHash;
use cubecl_environment::backtrace::BackTrace;
use cubecl_environment::collections::HashMap;
use cubecl_environment::persistence::{
CacheOption, Namespace, Store, StoreKey, StoreOptions, StoreValue,
};
use cubecl_ir::{ElemType, StorageType};
use thiserror::Error;
pub fn compilation_store<K: StoreKey, V: StoreValue>(
backend: &'static str,
fingerprint: impl AsRef<str>,
) -> Option<Store<K, V>> {
#[cfg(std_io)]
{
use crate::config::RuntimeConfig;
if !crate::config::CubeClRuntimeConfig::get().compilation.cache {
return None;
}
Some(Store::new(
StoreOptions::new()
.storage(Namespace::scoped(backend, fingerprint))
.cache(CacheOption::Lazy),
))
}
#[cfg(not(std_io))]
{
let _ = (backend, fingerprint);
None
}
}
pub fn store_compiled<K: StoreKey, V: StoreValue>(store: &mut Store<K, V>, key: K, value: V) {
if let Err(err) = store.insert(key, value) {
log::warn!("Unable to cache the compiled kernel: {}", err.reason());
}
}
pub trait CubeTask<C: Compiler>: KernelMetadata + Send + Sync {
fn define(&self) -> KernelDefinition;
fn compile(
&self,
definition: KernelDefinition,
compiler: &mut C,
compilation_options: &C::CompilationOptions,
mode: ExecutionMode,
address_type: StorageType,
) -> Result<CompiledKernel<C>, CompilationError>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct KernelCacheKey {
pub id: StableHash,
pub ir: StableHash,
}
impl KernelCacheKey {
pub fn new(id: &KernelId, definition: &KernelDefinition) -> Self {
Self {
id: id.stable_hash(),
ir: definition.stable_hash(),
}
}
}
#[derive(Debug)]
pub struct CompilationCache<K, V> {
entries: HashMap<K, V>,
generation: Option<u32>,
}
impl<K: Eq + Hash, V> CompilationCache<K, V> {
pub fn mirroring<SK: StoreKey, SV: StoreValue>(store: &Option<Store<SK, SV>>) -> Self {
Self {
entries: HashMap::new(),
generation: store
.is_some()
.then(cubecl_environment::environment::generation),
}
}
pub fn unbound() -> Self {
Self {
entries: HashMap::new(),
generation: None,
}
}
pub fn get(&mut self, key: &K) -> Option<&V> {
self.reset_if_switched();
self.entries.get(key)
}
pub fn contains(&mut self, key: &K) -> bool {
self.reset_if_switched();
self.entries.contains_key(key)
}
pub fn insert(&mut self, key: K, value: V) {
self.reset_if_switched();
self.entries.insert(key, value);
}
fn reset_if_switched(&mut self) {
let Some(generation) = self.generation else {
return;
};
let current = cubecl_environment::environment::generation();
if current == generation {
return;
}
log::debug!("Environment switched, dropping the in-memory compilation cache");
self.generation = Some(current);
self.entries.clear();
}
}
#[derive(Error, Clone)]
#[cfg_attr(std_io, derive(serde::Serialize, serde::Deserialize))]
pub enum CompilationError {
#[error(
"An unsupported instruction caused the compilation to fail\nCaused by:\n {reason}\nBacktrace:\n{backtrace}"
)]
UnsupportedInstruction {
reason: String,
#[cfg_attr(std_io, serde(skip))]
backtrace: BackTrace,
},
#[error(
"An error caused the compilation to fail\nCaused by:\n {reason}\nBacktrace:\n{backtrace}"
)]
Generic {
reason: String,
#[cfg_attr(std_io, serde(skip))]
backtrace: BackTrace,
},
#[error(
"A validation error caused the compilation to fail\nCaused by:\n {reason}\nBacktrace:\n{backtrace}"
)]
Validation {
reason: String,
#[cfg_attr(std_io, serde(skip))]
backtrace: BackTrace,
},
}
impl core::fmt::Debug for CompilationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{self}")
}
}
pub trait Compiler: Sync + Send + 'static + Clone + core::fmt::Debug {
type Representation: core::fmt::Display;
type CompilationOptions: Send + Default + core::fmt::Debug;
fn compile(
&mut self,
kernel: KernelDefinition,
compilation_options: &Self::CompilationOptions,
mode: ExecutionMode,
addr_type: StorageType,
) -> Result<Self::Representation, CompilationError>;
fn elem_size(&self, elem: ElemType) -> usize;
fn extension(&self) -> &'static str;
}