Skip to main content

cubecl_ir/dialect/
tma.rs

1use core::ops::RangeInclusive;
2
3use alloc::format;
4use cubecl_macros_internal::cube_op;
5use pliron::{derive::op_interface_impl, printable::Printable, verify_err};
6use thiserror::Error;
7
8use crate::{
9    AddressSpace,
10    attributes::IndexAttr,
11    interfaces::{MemoryEffect, MemoryEffects, TypeExt},
12    prelude::*,
13    types::{
14        PointerType,
15        barrier::{BarrierLevel, BarrierType},
16        cuda::TensorMapType,
17    },
18};
19
20#[derive(Error, Debug)]
21pub enum TmaOpError {
22    #[error("[TmaOp]: Invalid address space for {_0}. Expected {_1}, got {_2}.")]
23    InvalidAddressSpace(&'static str, &'static str, AddressSpace),
24    #[error("[TmaOp]: Unsupported rank {_0}, expected rank to be in range {_1:?}.")]
25    UnsupportedRank(usize, RangeInclusive<usize>),
26}
27
28fn expected_barrier_ty(ctx: &Context) -> TypeHandle {
29    PointerType::get(
30        ctx,
31        BarrierType::get(ctx, BarrierLevel::Cube).into(),
32        AddressSpace::Shared,
33    )
34    .to_handle()
35}
36
37#[pliron_op(name = "tma.load", format, attributes = (tma_load_rank: IndexAttr))]
38#[op_interfaces(AtLeastNOpdsInterface<4>, OperandNOfType<0, PointerType>, OperandNOfType<1, TensorMapType>, OperandNOfType<2, PointerType>)]
39pub struct TmaLoadOp;
40
41impl TmaLoadOp {
42    pub fn new(
43        ctx: &mut Context,
44        barrier: Value,
45        tensor_map: Value,
46        destination: Value,
47        indices: Vec<Value>,
48    ) -> Self {
49        let rank = indices.len();
50        let mut operands = vec![barrier, tensor_map, destination];
51        operands.extend(indices);
52        let op = Self {
53            op: Operation::new(
54                ctx,
55                Self::get_concrete_op_info(),
56                vec![],
57                operands,
58                vec![],
59                0,
60            ),
61        };
62        op.set_attr_tma_load_rank(ctx, rank.into());
63        op
64    }
65
66    pub fn barrier(&self, ctx: &Context) -> Value {
67        self.get_operation().deref(ctx).get_operand(0)
68    }
69
70    pub fn tensor_map(&self, ctx: &Context) -> Value {
71        self.get_operation().deref(ctx).get_operand(1)
72    }
73
74    pub fn destination(&self, ctx: &Context) -> Value {
75        self.get_operation().deref(ctx).get_operand(2)
76    }
77
78    pub fn indices(&self, ctx: &Context) -> Vec<Value> {
79        self.get_operation().deref(ctx).operands().skip(3).collect()
80    }
81
82    pub fn rank(&self, ctx: &Context) -> usize {
83        self.get_attr_tma_load_rank(ctx).unwrap().0
84    }
85}
86
87#[op_interface_impl]
88impl MemoryEffects for TmaLoadOp {
89    fn memory_effects(&self, ctx: &Context) -> Vec<MemoryEffect> {
90        vec![MemoryEffect::Write(self.destination(ctx))]
91    }
92}
93
94impl Verify for TmaLoadOp {
95    fn verify(&self, ctx: &Context) -> Result<()> {
96        let loc = self.loc(ctx);
97        let barrier_ty = self.barrier(ctx).get_type(ctx).as_ptr(ctx);
98        let dest_ty = self.barrier(ctx).get_type(ctx).as_ptr(ctx);
99
100        if !barrier_ty.inner.deref(ctx).is::<BarrierType>() {
101            let expected = expected_barrier_ty(ctx).deref(ctx);
102            return verify_err!(
103                loc,
104                OperandNOfTypeError::AllOperandsOfTypeVerifyErr(
105                    format!("{} {}", expected.get_type_id(), expected.disp(ctx)),
106                    format!("{} {}", barrier_ty.get_type_id(), barrier_ty.disp(ctx))
107                )
108            );
109        }
110
111        if dest_ty.address_space != AddressSpace::Shared {
112            return verify_err!(
113                loc,
114                TmaOpError::InvalidAddressSpace("destination", "Shared", dest_ty.address_space)
115            );
116        }
117
118        if !(1..=5).contains(&self.rank(ctx)) {
119            return verify_err!(loc, TmaOpError::UnsupportedRank(self.rank(ctx), 1..=5));
120        }
121
122        Ok(())
123    }
124}
125
126#[pliron_op(name = "tma.load_im2col", format, attributes = (tma_load_im2col_rank: IndexAttr))]
127#[op_interfaces(AtLeastNOpdsInterface<5>, OperandNOfType<0, PointerType>, OperandNOfType<1, TensorMapType>, OperandNOfType<2, PointerType>)]
128pub struct TmaLoadIm2colOp;
129
130impl TmaLoadIm2colOp {
131    pub fn new(
132        ctx: &mut Context,
133        barrier: Value,
134        tensor_map: Value,
135        destination: Value,
136        indices: Vec<Value>,
137        offsets: Vec<Value>,
138    ) -> Self {
139        let rank = indices.len();
140        let mut operands = vec![barrier, tensor_map, destination];
141        operands.extend(indices);
142        operands.extend(offsets);
143        let op = Self {
144            op: Operation::new(
145                ctx,
146                Self::get_concrete_op_info(),
147                vec![],
148                operands,
149                vec![],
150                0,
151            ),
152        };
153        op.set_attr_tma_load_im2col_rank(ctx, rank.into());
154        op
155    }
156
157    pub fn barrier(&self, ctx: &Context) -> Value {
158        self.get_operation().deref(ctx).get_operand(0)
159    }
160
161    pub fn tensor_map(&self, ctx: &Context) -> Value {
162        self.get_operation().deref(ctx).get_operand(1)
163    }
164
165    pub fn destination(&self, ctx: &Context) -> Value {
166        self.get_operation().deref(ctx).get_operand(2)
167    }
168
169    pub fn indices(&self, ctx: &Context) -> Vec<Value> {
170        let rank = self.get_attr_tma_load_im2col_rank(ctx).unwrap().0;
171        self.get_operation()
172            .deref(ctx)
173            .operands()
174            .skip(3)
175            .take(rank)
176            .collect()
177    }
178
179    pub fn offsets(&self, ctx: &Context) -> Vec<Value> {
180        let rank = self.get_attr_tma_load_im2col_rank(ctx).unwrap().0;
181        self.get_operation()
182            .deref(ctx)
183            .operands()
184            .skip(3 + rank)
185            .collect()
186    }
187
188    pub fn rank(&self, ctx: &Context) -> usize {
189        self.get_attr_tma_load_im2col_rank(ctx).unwrap().0
190    }
191}
192
193#[op_interface_impl]
194impl MemoryEffects for TmaLoadIm2colOp {
195    fn memory_effects(&self, ctx: &Context) -> Vec<MemoryEffect> {
196        vec![MemoryEffect::Write(self.destination(ctx))]
197    }
198}
199
200impl Verify for TmaLoadIm2colOp {
201    fn verify(&self, ctx: &Context) -> Result<()> {
202        let loc = self.loc(ctx);
203        let barrier_ty = self.barrier(ctx).get_type(ctx).as_ptr(ctx);
204        let dest_ty = self.barrier(ctx).get_type(ctx).as_ptr(ctx);
205
206        if !barrier_ty.inner.deref(ctx).is::<BarrierType>() {
207            let expected = expected_barrier_ty(ctx).deref(ctx);
208            return verify_err!(
209                loc,
210                OperandNOfTypeError::AllOperandsOfTypeVerifyErr(
211                    format!("{} {}", expected.get_type_id(), expected.disp(ctx)),
212                    format!("{} {}", barrier_ty.get_type_id(), barrier_ty.disp(ctx))
213                )
214            );
215        }
216
217        if dest_ty.address_space != AddressSpace::Shared {
218            return verify_err!(
219                loc,
220                TmaOpError::InvalidAddressSpace("destination", "Shared", dest_ty.address_space)
221            );
222        }
223
224        if !(3..=5).contains(&self.rank(ctx)) {
225            return verify_err!(loc, TmaOpError::UnsupportedRank(self.rank(ctx), 3..=5));
226        }
227
228        Ok(())
229    }
230}
231
232#[pliron_op(name = "tma.store", format, attributes = (tma_store_rank: IndexAttr))]
233#[op_interfaces(AtLeastNOpdsInterface<4>, OperandNOfType<0, PointerType>, OperandNOfType<1, TensorMapType>)]
234pub struct TmaStoreOp;
235
236impl TmaStoreOp {
237    pub fn new(ctx: &mut Context, source: Value, tensor_map: Value, indices: Vec<Value>) -> Self {
238        let rank = indices.len();
239        let mut operands = vec![source, tensor_map];
240        operands.extend(indices);
241        let op = Self {
242            op: Operation::new(
243                ctx,
244                Self::get_concrete_op_info(),
245                vec![],
246                operands,
247                vec![],
248                0,
249            ),
250        };
251        op.set_attr_tma_store_rank(ctx, rank.into());
252        op
253    }
254
255    pub fn source(&self, ctx: &Context) -> Value {
256        self.get_operation().deref(ctx).get_operand(0)
257    }
258
259    pub fn tensor_map(&self, ctx: &Context) -> Value {
260        self.get_operation().deref(ctx).get_operand(1)
261    }
262
263    pub fn indices(&self, ctx: &Context) -> Vec<Value> {
264        self.get_operation().deref(ctx).operands().skip(2).collect()
265    }
266
267    pub fn rank(&self, ctx: &Context) -> usize {
268        self.get_attr_tma_store_rank(ctx).unwrap().0
269    }
270}
271
272#[op_interface_impl]
273impl MemoryEffects for TmaStoreOp {
274    fn memory_effects(&self, ctx: &Context) -> Vec<MemoryEffect> {
275        vec![MemoryEffect::Read(self.source(ctx))]
276    }
277}
278
279impl Verify for TmaStoreOp {
280    fn verify(&self, ctx: &Context) -> Result<()> {
281        let loc = self.loc(ctx);
282        let src_ty = self.source(ctx).get_type(ctx).as_ptr(ctx);
283
284        if src_ty.address_space != AddressSpace::Shared {
285            return verify_err!(
286                loc,
287                TmaOpError::InvalidAddressSpace("source", "Shared", src_ty.address_space)
288            );
289        }
290
291        if !(1..=5).contains(&self.rank(ctx)) {
292            return verify_err!(loc, TmaOpError::UnsupportedRank(self.rank(ctx), 1..=5));
293        }
294
295        Ok(())
296    }
297}
298
299#[cube_op(name = "tma.commit_group")]
300#[result_ty(none)]
301pub struct CommitGroupOp {}
302
303#[cube_op(name = "tma.wait_group")]
304#[result_ty(none)]
305pub struct WaitGroupOp {
306    pub max_pending: IndexAttr,
307}
308
309#[cube_op(name = "tma.wait_group_read")]
310#[result_ty(none)]
311pub struct WaitGroupReadOp {
312    pub max_pending: IndexAttr,
313}