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