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