1use crate::{CubeRuntime, tensor::CubeTensor};
2use burn_backend::cubecl::dtype_to_storage_type;
3use burn_backend::{
4 Backend, BackendGraph, BackendTypes, DTypeUsage, DTypeUsageSet, DeviceOps, ExecutionError,
5 TensorData,
6};
7use burn_std::{BoolStore, DType};
8use cubecl::{
9 features::{MmaConfig, TypeUsage},
10 server::ComputeServer,
11};
12use std::marker::PhantomData;
13
14#[cfg(not(feature = "fusion"))]
15use burn_backend::tensor::{BoolTensor, FloatTensor, IntTensor, QuantizedTensor};
16#[cfg(not(feature = "fusion"))]
17use burn_ir::{BackendIr, TensorHandle};
18
19fn graph_err(err: impl core::fmt::Display) -> ExecutionError {
21 ExecutionError::WithContext {
22 reason: format!("{err}"),
23 }
24}
25
26#[derive(new)]
28pub struct CubeBackend<R: CubeRuntime> {
29 _runtime: PhantomData<R>,
30}
31
32impl<R> BackendTypes for CubeBackend<R>
33where
34 R: CubeRuntime,
35 R::Server: ComputeServer,
36 R::Device: DeviceOps,
37{
38 type Device = R::Device;
39
40 type FloatTensorPrimitive = CubeTensor<R>;
41 type IntTensorPrimitive = CubeTensor<R>;
42 type BoolTensorPrimitive = CubeTensor<R>;
43 type QuantizedTensorPrimitive = CubeTensor<R>;
44
45 type GraphPrimitive = cubecl::client::Graph<R>;
46}
47
48impl<R> Backend for CubeBackend<R>
49where
50 R: CubeRuntime,
51 R::Server: ComputeServer,
52 R::Device: DeviceOps,
53{
54 fn name(device: &Self::Device) -> String {
55 let client = R::client(device);
56 format!("cubecl<{}>", R::name(&client))
57 }
58
59 fn seed(_device: &Self::Device, seed: u64) {
60 cubek::random::seed(seed);
61 }
62
63 fn ad_enabled(_device: &Self::Device) -> bool {
64 false
65 }
66
67 fn sync(device: &Self::Device) -> Result<(), ExecutionError> {
68 let client = R::client(device);
69 futures_lite::future::block_on(client.sync()).map_err(|err| ExecutionError::WithContext {
70 reason: format!("{err}"),
71 })
72 }
73
74 fn graph_prepare(device: &Self::Device) -> Result<(), ExecutionError> {
75 let client = R::client(device);
76 client.graph_prepare().map_err(graph_err)
77 }
78
79 fn graph_start_capture(device: &Self::Device) -> Result<(), ExecutionError> {
80 let client = R::client(device);
81 client.start_capture().map_err(graph_err)
82 }
83
84 fn graph_stop_capture(device: &Self::Device) -> Result<BackendGraph<Self>, ExecutionError> {
85 let client = R::client(device);
86 client.stop_capture().map_err(graph_err)
87 }
88
89 unsafe fn graph_replay(
90 _device: &Self::Device,
91 graph: &BackendGraph<Self>,
92 ) -> Result<(), ExecutionError> {
93 unsafe { graph.replay() };
100 Ok(())
101 }
102
103 fn memory_persistent_allocations<
104 Output: Send,
105 Input: Send,
106 Func: Fn(Input) -> Output + Send,
107 >(
108 device: &Self::Device,
109 input: Input,
110 func: Func,
111 ) -> Output {
112 let client = R::client(device);
113 client.memory_persistent_allocation(input, func).unwrap()
114 }
115
116 fn memory_cleanup(device: &Self::Device) {
117 let client = R::client(device);
118 client.memory_cleanup();
119 }
120
121 fn staging<'a, Iter>(data: Iter, device: &Self::Device)
122 where
123 Iter: Iterator<Item = &'a mut TensorData>,
124 {
125 let client = R::client(device);
126 client.staging(data.map(|td| &mut td.bytes), false);
127 }
128
129 fn supports_dtype(device: &Self::Device, dtype: DType) -> bool {
130 if let DType::Bool(BoolStore::Native) = dtype {
133 return false;
134 }
135
136 let client = R::client(device);
137
138 let type_usage = client.properties().type_usage(dtype_to_storage_type(dtype));
139 type_usage.is_superset(
141 TypeUsage::Buffer
142 | TypeUsage::Conversion
143 | TypeUsage::Arithmetic
144 | TypeUsage::DotProduct,
145 )
146 }
147
148 fn dtype_usage(device: &Self::Device, dtype: DType) -> DTypeUsageSet {
149 if let DType::Bool(BoolStore::Native) = dtype {
152 return DTypeUsageSet::empty();
153 }
154
155 let client = R::client(device);
156
157 let props = client.properties();
158 let storage = dtype_to_storage_type(dtype);
159 let usage = props.type_usage(storage);
160
161 let mut out = DTypeUsageSet::new();
162
163 if usage.is_superset(TypeUsage::Buffer | TypeUsage::Conversion) {
164 out |= DTypeUsage::Storage;
165 }
166
167 if usage.contains(TypeUsage::Arithmetic) {
168 out |= DTypeUsage::Arithmetic;
169 }
170
171 let has_mma = |cfg: &MmaConfig| {
172 cfg.a_type == storage || cfg.b_type == storage || cfg.cd_type == storage
173 };
174 if props.features.matmul.cmma.iter().any(has_mma)
175 || props.features.matmul.mma.iter().any(has_mma)
176 {
177 out |= DTypeUsage::Accelerated;
178 }
179
180 out
181 }
182
183 fn device_count(type_id: u16) -> usize {
184 let client = R::client(&Default::default());
185 client.device_count(type_id)
186 }
187
188 fn flush(device: &Self::Device) {
189 let client = R::client(device);
190 client.flush().unwrap();
191 }
192}
193
194impl<R: CubeRuntime> core::fmt::Debug for CubeBackend<R> {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 f.write_str("CubeCLBackend")
197 }
198}
199
200impl<R: CubeRuntime> Clone for CubeBackend<R> {
201 fn clone(&self) -> Self {
202 Self::new()
203 }
204}
205
206impl<R: CubeRuntime> Default for CubeBackend<R> {
207 fn default() -> Self {
208 Self::new()
209 }
210}
211
212impl<R: cubecl::Runtime> CubeRuntime for R
213where
214 R::Device: DeviceOps,
215{
216 type CubeDevice = R::Device;
217 type CubeServer = R::Server;
218}
219
220#[cfg(not(feature = "fusion"))]
221impl<R: CubeRuntime> BackendIr for CubeBackend<R> {
222 type Handle = CubeTensor<R>;
223
224 fn float_tensor(handle: TensorHandle<Self::Handle>) -> FloatTensor<Self> {
225 handle.handle
226 }
227
228 fn int_tensor(handle: TensorHandle<Self::Handle>) -> IntTensor<Self> {
229 handle.handle
230 }
231
232 fn bool_tensor(handle: TensorHandle<Self::Handle>) -> BoolTensor<Self> {
233 handle.handle
234 }
235
236 fn quantized_tensor(handle: TensorHandle<Self::Handle>) -> QuantizedTensor<Self> {
237 handle.handle
238 }
239
240 fn float_tensor_handle(tensor: FloatTensor<Self>) -> Self::Handle {
241 tensor
242 }
243
244 fn int_tensor_handle(tensor: IntTensor<Self>) -> Self::Handle {
245 tensor
246 }
247
248 fn bool_tensor_handle(tensor: BoolTensor<Self>) -> Self::Handle {
249 tensor
250 }
251
252 fn quantized_tensor_handle(tensor: QuantizedTensor<Self>) -> Self::Handle {
253 tensor
254 }
255}