1use alloc::vec;
4use pliron::{r#type::TypeHandle, value::Value};
5
6use crate as cubecl;
7use cubecl_ir::{
8 ExpandValue,
9 dialect::tma::*,
10 types::barrier::{BarrierLevel, BarrierType},
11};
12use cubecl_macros::intrinsic;
13use paste::paste;
14
15use crate::{
16 ir::{Scope, dialect::barrier::*},
17 prelude::*,
18 unexpanded,
19};
20
21use super::{CubePrimitive, CubeType, NativeExpand, SliceExpand, TensorMap};
22
23#[derive(Clone, Copy, PartialEq, Eq)]
26pub struct Barrier;
27pub type BarrierExpand = NativeExpand<Barrier>;
28
29#[derive(Clone, Copy, PartialEq)]
30pub struct BarrierToken;
31
32impl CubeType for Barrier {
33 type ExpandType = NativeExpand<Barrier>;
34}
35
36impl CubeDebug for Barrier {}
37
38impl CubePrimitive for Barrier {
39 type Scalar = u32; type Size = Const<1>;
41 type WithScalar<S: Scalar> = S;
42 fn from_const_value(_value: cubecl_ir::ConstantValue) -> Self {
43 unreachable!("Can't create from const value")
44 }
45}
46
47impl ConstantTyped for Barrier {
48 fn __expand_as_type(scope: &Scope) -> TypeHandle {
49 BarrierType::get(scope.ctx(), BarrierLevel::Cube).into()
50 }
51}
52
53impl NativeAssign for Barrier {
54 fn elem_init_mut(_scope: &Scope, elem: ExpandValue) -> ExpandValue {
55 elem
56 }
57}
58
59impl CubeType for BarrierToken {
60 type ExpandType = NativeExpand<BarrierToken>;
61}
62
63impl ReadValue for NativeExpand<BarrierToken> {
64 fn read_value(&self, scope: &Scope) -> Value {
65 self.expand.read_value(scope)
66 }
67}
68
69impl NativeAssign for BarrierToken {
70 fn elem_init_mut(_scope: &Scope, elem: ExpandValue) -> ExpandValue {
71 elem
72 }
73}
74
75impl AsMutExpand for NativeExpand<BarrierToken> {
76 fn __expand_ref_mut_method(&mut self, _: &Scope) -> &mut Self {
77 self
78 }
79}
80
81macro_rules! tensor_map_load {
82 ($dim: literal, $($arg: expr),*) => {
83 paste! {
84 impl Barrier {
85 #[allow(unused, clippy::too_many_arguments)]
88 pub fn [<tma_load_ $dim d>]<C1: CubePrimitive, C2: CubePrimitive<Scalar = C1::Scalar>>(
89 &self,
90 source: &TensorMap<C1, Tiled>,
91 destination: &mut [C2],
92 $($arg: i32),*
93 ) {
94 unexpanded!()
95 }
96
97 #[allow(clippy::too_many_arguments)]
98 pub fn [<__expand_tma_load_ $dim d>]<C1: CubePrimitive, C2: CubePrimitive<Scalar = C1::Scalar>>(
99 scope: &Scope,
100 expand: &NativeExpand<Barrier>,
101 source: &NativeExpand<TensorMap<C1, Tiled>>,
102 destination: &mut SliceExpand<C2>,
103 $($arg: NativeExpand<i32>),*
104 ) {
105 expand.[<__expand_tma_load_ $dim d_method>](scope, source, destination, $($arg),*);
106 }
107 }
108
109 impl NativeExpand<Barrier> {
110 #[allow(clippy::too_many_arguments)]
111 pub fn [<__expand_tma_load_ $dim d_method>]<C1: CubePrimitive, C2: CubePrimitive<Scalar = C1::Scalar>>(
112 &self,
113 scope: &Scope,
114 source: &NativeExpand<TensorMap<C1, Tiled>>,
115 destination: &mut SliceExpand<C2>,
116 $($arg: NativeExpand<i32>),*
117 ) {
118 let barrier = self.value(scope);
119 let source = source.value(scope);
120 let destination = unsafe { *destination.__expand_as_ptr_method(scope) }.value(scope);
121 let indices = vec![$($arg.read_value(scope)),*];
122
123 let mem_copy = TmaLoadOp::new(scope.ctx_mut(), barrier, source, destination, indices);
124 scope.register(&mem_copy);
125 }
126 }
127 }
128 };
129}
130
131macro_rules! tensor_map_load_im2col {
132 ($dim: literal, $($arg: expr),*; $($offset: expr),*) => {
133 paste! {
134 impl Barrier {
135 #[allow(unused, clippy::too_many_arguments)]
138 pub fn [<tma_load_im2col_ $dim d>]<C1: CubePrimitive, C2: CubePrimitive<Scalar = C1::Scalar>>(
139 &self,
140 source: &TensorMap<C1, Im2col>,
141 destination: &mut [C2],
142 $($arg: i32,)*
143 $($offset: u16),*
144 ) {
145 unexpanded!()
146 }
147
148 #[allow(clippy::too_many_arguments)]
149 pub fn [<__expand_tma_load_im2col_ $dim d>]<C1: CubePrimitive, C2: CubePrimitive<Scalar = C1::Scalar>>(
150 scope: &Scope,
151 expand: &NativeExpand<Barrier>,
152 source: &NativeExpand<TensorMap<C1, Im2col>>,
153 destination: &mut SliceExpand<C2>,
154 $($arg: NativeExpand<i32>,)*
155 $($offset: NativeExpand<u16>),*
156 ) {
157 expand.[<__expand_tma_load_im2col_ $dim d_method>](scope, source, destination, $($arg),*, $($offset),*);
158 }
159 }
160
161 impl NativeExpand<Barrier> {
162 #[allow(clippy::too_many_arguments)]
163 pub fn [<__expand_tma_load_im2col_ $dim d_method>]<C1: CubePrimitive, C2: CubePrimitive<Scalar = C1::Scalar>>(
164 &self,
165 scope: &Scope,
166 source: &NativeExpand<TensorMap<C1, Im2col>>,
167 destination: &mut SliceExpand<C2>,
168 $($arg: NativeExpand<i32>,)*
169 $($offset: NativeExpand<u16>),*
170 ) {
171 let barrier = self.value(scope);
172 let source = source.value(scope);
173 let destination = unsafe { *destination.__expand_as_ptr_method(scope) }.value(scope);
174 let indices = vec![$($arg.read_value(scope)),*];
175 let offsets = vec![$($offset.read_value(scope)),*];
176
177 let mem_copy = TmaLoadIm2colOp::new(scope.ctx_mut(), barrier, source, destination, indices, offsets);
178 scope.register(&mem_copy);
179 }
180 }
181 }
182 };
183}
184
185tensor_map_load!(1, x);
186tensor_map_load!(2, y, x);
187tensor_map_load!(3, z, y, x);
188tensor_map_load!(4, w, z, y, x);
189tensor_map_load!(5, v, w, z, y, x);
190
191tensor_map_load_im2col!(3, n, w, c; w_offset);
192tensor_map_load_im2col!(4, n, h, w, c; h_offset, w_offset);
193tensor_map_load_im2col!(5, n, d, h, w, c; d_offset, h_offset, w_offset);
194
195#[cube]
196impl Barrier {
197 pub fn local() -> Self {
200 intrinsic!(|scope| {
201 let value =
202 scope.create_local_mut(BarrierType::get(scope.ctx(), BarrierLevel::Unit), None);
203 let arrival_count: ExpandValue = 1u32.into();
204 let arrival_count = arrival_count.read_value(scope);
205 let op = InitOp::new(scope.ctx_mut(), value, arrival_count);
206 scope.register(&op);
207 value.into()
208 })
209 }
210
211 pub fn shared(arrival_count: u32, is_elected: bool) -> Shared<Barrier> {
222 intrinsic!(|scope| {
223 if is_elected.expand.as_const().is_some() {
224 scope.push_warning(
225 "Barrier::shared: election must select exactly one unit; use `UNIT_POS == 0` instead of a constant",
226 );
227 }
228 let value =
229 scope.create_shared(BarrierType::get(scope.ctx(), BarrierLevel::Cube), None);
230 if_expand(scope, is_elected, |scope| {
231 let arrival_count = arrival_count.read_value(scope);
232 let op = InitOp::new(scope.ctx_mut(), value, arrival_count);
233 scope.register(&op);
234 });
235 sync_cube::expand(scope);
236 value.into()
237 })
238 }
239
240 pub fn shared_uninit() -> Shared<Barrier> {
243 intrinsic!(|scope| {
244 let value =
245 scope.create_shared(BarrierType::get(scope.ctx(), BarrierLevel::Cube), None);
246 value.into()
247 })
248 }
249
250 pub fn init_manual(&self, arrival_count: u32) {
263 intrinsic!(|scope| {
264 let barrier = self.value(scope);
265 let arrival_count = arrival_count.read_value(scope);
266 let op = InitOp::new(scope.ctx_mut(), barrier, arrival_count);
267 scope.register(&op);
268 })
269 }
270}
271
272#[cube]
275impl Barrier {
276 pub fn memcpy_async<C: CubePrimitive>(&self, source: &[C], destination: &mut [C]) {
283 intrinsic!(|scope| {
284 let barrier = self.value(scope);
285 let source_length = source.__extract_length(scope).value(scope);
286 let source = unsafe { *source.__expand_as_ptr_method(scope) }.value(scope);
287 let destination = unsafe { *destination.__expand_as_ptr_method(scope) }.value(scope);
288
289 let mem_copy = MemCopyAsyncOp::new(
290 scope.ctx_mut(),
291 barrier,
292 source,
293 destination,
294 source_length,
295 false,
296 );
297
298 scope.register(&mem_copy);
299 })
300 }
301
302 pub fn memcpy_async_cooperative<C: CubePrimitive>(&self, source: &[C], destination: &mut [C]) {
309 intrinsic!(|scope| {
310 let barrier = self.value(scope);
311 let source_length = source.__extract_length(scope).value(scope);
312 let source = unsafe { *source.__expand_as_ptr_method(scope) }.value(scope);
313 let destination = unsafe { *destination.__expand_as_ptr_method(scope) }.value(scope);
314
315 let mem_copy = MemCopyAsyncOp::new(
316 scope.ctx_mut(),
317 barrier,
318 source,
319 destination,
320 source_length,
321 true,
322 );
323
324 scope.register(&mem_copy);
325 })
326 }
327
328 pub fn memcpy_async_tx<C: CubePrimitive>(&self, source: &[C], destination: &mut [C]) {
336 intrinsic!(|scope| {
337 let barrier = self.value(scope);
338 let source_length = source.__extract_length(scope).value(scope);
339 let source = unsafe { *source.__expand_as_ptr_method(scope) }.value(scope);
340 let destination = unsafe { *destination.__expand_as_ptr_method(scope) }.value(scope);
341
342 let mem_copy =
343 MemCopyAsyncTxOp::new(scope.ctx_mut(), barrier, source, destination, source_length);
344
345 scope.register(&mem_copy);
346 })
347 }
348}
349
350#[cube]
353impl Barrier {
354 pub fn arrive(&self) -> BarrierToken {
356 intrinsic!(|scope| {
357 let barrier = self.value(scope);
358 let arrive = ArriveOp::new(scope.ctx_mut(), barrier);
359 scope.register_with_result(&arrive).into()
360 })
361 }
362
363 pub fn arrive_and_expect_tx(&self, arrival_count: u32, transaction_count: u32) -> BarrierToken {
365 intrinsic!(|scope| {
366 let barrier = self.value(scope);
367 let arrival_count = arrival_count.read_value(scope);
368 let transaction_count = transaction_count.read_value(scope);
369 let op = ArriveAndExpectTxOp::new(
370 scope.ctx_mut(),
371 barrier,
372 arrival_count,
373 transaction_count,
374 );
375 scope.register_with_result(&op).into()
376 })
377 }
378
379 pub fn expect_tx(&self, transaction_count_update: u32) {
381 intrinsic!(|scope| {
382 let barrier = self.value(scope);
383 let transaction_count_update = transaction_count_update.value(scope);
384 scope.register(&ExpectTxOp::new(
385 scope.ctx_mut(),
386 barrier,
387 transaction_count_update,
388 ));
389 })
390 }
391
392 pub fn arrive_and_wait(&self) {
394 intrinsic!(|scope| {
395 let barrier = self.value(scope);
396 scope.register(&ArriveAndWaitOp::new(scope.ctx_mut(), barrier));
397 })
398 }
399
400 pub fn wait(&self, token: BarrierToken) {
402 intrinsic!(|scope| {
403 let barrier = self.value(scope);
404 let token = token.value(scope);
405 scope.register(&WaitOp::new(scope.ctx_mut(), barrier, token));
406 })
407 }
408
409 pub fn wait_parity(&self, phase: u32) {
412 intrinsic!(|scope| {
413 let barrier = self.value(scope);
414 let phase = phase.read_value(scope);
415 scope.register(&WaitParityOp::new(scope.ctx_mut(), barrier, phase));
416 })
417 }
418}
419
420pub fn copy_async<C: CubePrimitive>(_source: &[C], _destination: &mut [C], _copy_size: u32) {
432 unexpanded!()
433}
434
435pub mod copy_async {
436 use super::*;
437
438 pub fn expand<C: CubePrimitive>(
439 scope: &Scope,
440 source: &SliceExpand<C>,
441 destination: &mut SliceExpand<C>,
442 copy_length: u32,
443 ) {
444 let source = unsafe { *source.__expand_as_ptr_method(scope) }.value(scope);
445 let destination = unsafe { *destination.__expand_as_ptr_method(scope) }.value(scope);
446 let scalar_size = C::Scalar::__expand_size(scope);
447 let copy_length_bytes = copy_length as usize * scalar_size;
448 let source_length = ExpandValue::from(copy_length_bytes).read_value(scope);
449
450 let mem_copy = CopyAsyncOp::new(
451 scope.ctx_mut(),
452 source,
453 destination,
454 source_length,
455 copy_length_bytes,
456 false,
457 );
458
459 scope.register(&mem_copy);
460 }
461}
462
463pub fn copy_async_checked<C: CubePrimitive>(
475 _source: &[C],
476 _destination: &mut [C],
477 _copy_size: u32,
478) {
479 unexpanded!();
480}
481
482pub mod copy_async_checked {
483 use super::*;
484
485 pub fn expand<C: CubePrimitive>(
486 scope: &Scope,
487 source: &SliceExpand<C>,
488 destination: &mut SliceExpand<C>,
489 copy_length: u32,
490 ) {
491 let source_length = source.__extract_length(scope);
492
493 let source = unsafe { *source.__expand_as_ptr_unchecked_method(scope) }.value(scope);
494 let destination =
495 unsafe { *destination.__expand_as_ptr_unchecked_method(scope) }.value(scope);
496 let scalar_size = C::Scalar::__expand_size(scope);
497 let vector_size = C::__expand_size(scope).__expand_runtime_method(scope);
498 let source_length_bytes = source_length.__expand_mul_method(scope, vector_size);
499
500 let mem_copy = CopyAsyncOp::new(
501 scope.ctx_mut(),
502 source,
503 destination,
504 source_length_bytes.read_value(scope),
505 copy_length as usize * scalar_size,
506 true,
507 );
508
509 scope.register(&mem_copy);
510 }
511}
512
513#[cube]
514impl Barrier {
515 pub fn commit_copy_async(&self) {
522 intrinsic!(|scope| {
523 let barrier = self.value(scope);
524 scope.register(&CommitCopyAsyncOp::new(scope.ctx_mut(), barrier));
525 })
526 }
527}
528
529impl From<SharedExpand<Barrier>> for BarrierExpand {
530 fn from(value: SharedExpand<Barrier>) -> Self {
531 value.expand.into()
532 }
533}
534
535#[cfg(test)]
536mod tests {
537 use super::*;
538 use cubecl_ir::{
539 AddressType,
540 settings::{Dim3, ExecutionMode, KernelSettings},
541 types::scalar::BoolType,
542 };
543
544 #[test]
545 fn shared_barrier_warns_on_a_constant_election() {
546 let scope = Scope::root(KernelSettings::new(
547 Dim3::new_1d(64),
548 ExecutionMode::Unchecked,
549 AddressType::U32,
550 ));
551 let _ = Barrier::__expand_shared(&scope, 64u32.into(), true.into());
552 let warnings = scope.pop_warnings();
553 assert_eq!(warnings.len(), 1, "{warnings:?}");
554 assert!(warnings[0].contains("UNIT_POS == 0"), "{warnings:?}");
555 }
556
557 #[test]
558 fn shared_barrier_is_quiet_for_a_runtime_election() {
559 let scope = Scope::root(KernelSettings::new(
560 Dim3::new_1d(64),
561 ExecutionMode::Unchecked,
562 AddressType::U32,
563 ));
564 let elected: NativeExpand<bool> =
565 ExpandValue::from(scope.create_local_mut(BoolType::get(scope.ctx()), None)).into();
566 let _ = Barrier::__expand_shared(&scope, 64u32.into(), elected);
567 assert!(scope.pop_warnings().is_empty());
568 }
569}