use cubecl_core::ir::nvidia::SmArch;
use cubecl_core::prelude::KernelDefinition;
use cubecl_cpp::shared::CompilationOptions;
use cubecl_cpp::{ComputeKernel, shared::CppCompiler, target::Cuda};
use cubecl_llvm::nvptx::ptx_version::PtxVersion;
use cubecl_server::compiler::{CompilationError, Compiler};
use cubecl_server::kernel::BufferIOAttr;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CudaBackend {
Cpp,
Llvm,
}
impl Default for CudaBackend {
fn default() -> Self {
if cfg!(feature = "cpp") {
CudaBackend::Cpp
} else {
CudaBackend::Llvm
}
}
}
#[derive(Clone, Debug)]
pub enum CudaCompiler {
Cpp(CppCompiler<Cuda>),
Llvm(cubecl_llvm::PlironCompiler),
}
impl CudaCompiler {
pub fn new(backend: CudaBackend) -> Self {
match backend {
CudaBackend::Cpp => CudaCompiler::Cpp(CppCompiler::default()),
CudaBackend::Llvm => CudaCompiler::Llvm(cubecl_llvm::PlironCompiler {
target: cubecl_llvm::LlvmTarget::Nvptx,
}),
}
}
}
impl Default for CudaCompiler {
fn default() -> Self {
Self::new(CudaBackend::default())
}
}
#[derive(Debug, Default, Clone)]
pub struct CudaCompilationOptions {
pub cpp: CompilationOptions,
pub arch: Option<SmArch>,
pub ptx_version: Option<PtxVersion>,
}
pub enum CudaRepresentation {
Cpp(ComputeKernel),
Llvm(cubecl_llvm::NvptxModule),
}
impl core::fmt::Debug for CudaRepresentation {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CudaRepresentation::Cpp(_) => f.write_str("CudaRepresentation::Cpp"),
CudaRepresentation::Llvm(module) => f
.debug_tuple("CudaRepresentation::Llvm")
.field(module)
.finish(),
}
}
}
impl CudaRepresentation {
pub fn shared_memory_size(&self) -> usize {
match self {
CudaRepresentation::Cpp(kernel) => kernel.shared_memory_size,
CudaRepresentation::Llvm(module) => module.shared_memory_size,
}
}
}
impl core::fmt::Display for CudaRepresentation {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
CudaRepresentation::Cpp(kernel) => write!(f, "{kernel}"),
CudaRepresentation::Llvm(module) => write!(f, "{}", module.ir),
}
}
}
impl Compiler for CudaCompiler {
type Representation = CudaRepresentation;
type CompilationOptions = CudaCompilationOptions;
fn buffer_io(repr: &Self::Representation) -> Option<Vec<BufferIOAttr>> {
match repr {
CudaRepresentation::Cpp(kernel) => <CppCompiler<Cuda> as Compiler>::buffer_io(kernel),
CudaRepresentation::Llvm(module) => Some(module.io.clone()),
}
}
fn compile(
&mut self,
kernel: KernelDefinition,
options: &Self::CompilationOptions,
) -> Result<Self::Representation, CompilationError> {
match self {
CudaCompiler::Cpp(compiler) => Ok(CudaRepresentation::Cpp(
compiler.compile(kernel, &options.cpp)?,
)),
CudaCompiler::Llvm(compiler) => {
let pliron_options = cubecl_llvm::PlironOptions {
arch: None,
sm_arch: options.arch,
ptx_version: options.ptx_version,
grid_constants: options.cpp.supports_features.grid_constants,
..Default::default()
};
match compiler.compile(kernel, &pliron_options)? {
cubecl_llvm::PlironArtifact::NvptxCode(module) => {
Ok(CudaRepresentation::Llvm(module))
}
_ => unreachable!("the CUDA runtime always configures LlvmTarget::Nvptx"),
}
}
}
}
fn extension(&self) -> &'static str {
match self {
CudaCompiler::Cpp(compiler) => compiler.extension(),
CudaCompiler::Llvm(_) => "ll",
}
}
fn lang_tag(&self) -> &'static str {
match self {
CudaCompiler::Cpp(compiler) => compiler.lang_tag(),
CudaCompiler::Llvm(compiler) => compiler.lang_tag(),
}
}
}