1use core::fmt;
2
3use alloc::vec::Vec;
4
5use cubecl_macros_internal::{NamedRewrite, cube_op};
6use pliron::{
7 builtin::attributes::IdentifierAttr,
8 combine::{Parser, parser::char::char},
9 identifier::Identifier,
10 input_err,
11 irfmt::parsers::{process_parsed_ssa_defs, spaced, ssa_opd_parse},
12 location::Location,
13 op::OpObj,
14 parsable::{self, IntoParseResult, Parsable, ParseResult},
15 printable::{self, Printable},
16};
17
18use crate::{
19 CanMaterialize,
20 dialect::{
21 matrix::{self, MatrixLayoutAttr, parse_closure, print_closure},
22 memory,
23 synchronization::SyncScope,
24 },
25 interfaces::{TypedExt, synchronizes},
26 prelude::*,
27};
28
29#[cube_op(name = "ssa_matrix.fill")]
33#[result_ty(argument)]
34#[op_traits(CanMaterialize)]
35pub struct FillOp {
36 pub value: Value,
37}
38
39#[op_interface_impl]
40impl MatrixToSSAOp for matrix::FillOp {
41 fn to_owned_matrix(
42 &self,
43 ctx: &mut Context,
44 rewriter: &mut DialectConversionRewriter,
45 _: &OperandsInfo,
46 ) -> Result<()> {
47 let value = self.value(ctx);
48 let out_ty = self.matrix(ctx).unwrap_ptr(ctx);
49 let op = FillOp::new(ctx, out_ty, value);
50 let matrix = rewriter.append_op_with_result(ctx, &op);
51 store_value(self.matrix(ctx), matrix, ctx, rewriter);
52 rewriter.erase_operation(ctx, self.get_operation());
53
54 Ok(())
55 }
56}
57
58#[cube_op(name = "ssa_matrix.load")]
59#[result_ty(argument)]
60#[op_traits(CanMaterialize)]
61pub struct LoadOp {
62 #[operand(ptr_read)]
63 pub source: Value,
64 pub stride: Value,
65 pub layout: MatrixLayoutAttr,
66}
67synchronizes!(LoadOp, SyncScope::Plane);
68
69#[op_interface_impl]
70impl MatrixToSSAOp for matrix::LoadOp {
71 fn to_owned_matrix(
72 &self,
73 ctx: &mut Context,
74 rewriter: &mut DialectConversionRewriter,
75 _: &OperandsInfo,
76 ) -> Result<()> {
77 let source = self.source(ctx);
78 let stride = self.stride(ctx);
79 let layout = self.layout(ctx).0;
80 let out_ty = self.matrix(ctx).unwrap_ptr(ctx);
81 let op = LoadOp::new(ctx, out_ty, source, stride, layout);
82 let matrix = rewriter.append_op_with_result(ctx, &op);
83 store_value(self.matrix(ctx), matrix, ctx, rewriter);
84 rewriter.erase_operation(ctx, self.get_operation());
85
86 Ok(())
87 }
88}
89
90#[cube_op(name = "ssa_matrix.store")]
91#[result_ty(none)]
92#[op_traits(CanMaterialize)]
93pub struct StoreOp {
94 pub matrix: Value,
95 #[operand(ptr_write)]
96 pub destination: Value,
97 pub stride: Value,
98 pub layout: MatrixLayoutAttr,
99}
100synchronizes!(StoreOp, SyncScope::Plane);
101
102#[op_interface_impl]
103impl MatrixToSSAOp for matrix::StoreOp {
104 fn to_owned_matrix(
105 &self,
106 ctx: &mut Context,
107 rewriter: &mut DialectConversionRewriter,
108 _: &OperandsInfo,
109 ) -> Result<()> {
110 let matrix = load_value(self.matrix(ctx), ctx, rewriter);
111 let dest = self.destination(ctx);
112 let stride = self.stride(ctx);
113 let layout = self.layout(ctx).0;
114 let op = StoreOp::new(ctx, matrix, dest, stride, layout);
115 rewriter.append_op(ctx, &op);
116 rewriter.erase_operation(ctx, self.get_operation());
117
118 Ok(())
119 }
120}
121
122#[cube_op(name = "ssa_matrix.multiply_accumulate")]
123#[result_ty(argument)]
124#[op_traits(CanMaterialize)]
125pub struct MultiplyAccumulateOp {
126 pub mat_a: Value,
127 pub mat_b: Value,
128 pub mat_c: Value,
129}
130synchronizes!(MultiplyAccumulateOp, SyncScope::Plane);
131
132#[op_interface_impl]
133impl MatrixToSSAOp for matrix::MultiplyAccumulateOp {
134 fn to_owned_matrix(
135 &self,
136 ctx: &mut Context,
137 rewriter: &mut DialectConversionRewriter,
138 _: &OperandsInfo,
139 ) -> Result<()> {
140 let mat_a = load_value(self.mat_a(ctx), ctx, rewriter);
141 let mat_b = load_value(self.mat_b(ctx), ctx, rewriter);
142 let mat_c = load_value(self.mat_c(ctx), ctx, rewriter);
143 let out_ty = self.mat_d(ctx).unwrap_ptr(ctx);
144 let op = MultiplyAccumulateOp::new(ctx, out_ty, mat_a, mat_b, mat_c);
145 let matrix_out = rewriter.append_op_with_result(ctx, &op);
146 store_value(self.mat_d(ctx), matrix_out, ctx, rewriter);
147 rewriter.erase_operation(ctx, self.get_operation());
148
149 Ok(())
150 }
151}
152
153#[cube_op(name = "ssa_matrix.cast")]
157#[result_ty(argument)]
158#[op_traits(CanMaterialize)]
159pub struct CastOp {
160 pub input: Value,
161}
162
163#[op_interface_impl]
164impl MatrixToSSAOp for matrix::CastOp {
165 fn to_owned_matrix(
166 &self,
167 ctx: &mut Context,
168 rewriter: &mut DialectConversionRewriter,
169 _: &OperandsInfo,
170 ) -> Result<()> {
171 let matrix_in = load_value(self.input(ctx), ctx, rewriter);
172 let out_ty = self.output(ctx).unwrap_ptr(ctx);
173 let op = CastOp::new(ctx, out_ty, matrix_in);
174 let matrix_out = rewriter.append_op_with_result(ctx, &op);
175 store_value(self.output(ctx), matrix_out, ctx, rewriter);
176 rewriter.erase_operation(ctx, self.get_operation());
177
178 Ok(())
179 }
180}
181
182#[pliron_op(
186 name = "ssa_matrix.elementwise",
187 attributes = (ssa_matrix_elementwise_closure: IdentifierAttr),
188 verifier = "succ"
189)]
190#[op_interfaces(OneResultInterface)]
191#[op_traits(CanMaterialize)]
192pub struct ElementwiseOp;
193
194impl ElementwiseOp {
195 pub fn new(
196 ctx: &mut Context,
197 matrix_in: Value,
198 closure: Identifier,
199 captures: Vec<Value>,
200 ) -> Self {
201 let out_ty = vec![matrix_in.get_type(ctx)];
202 let mut opds = vec![matrix_in];
203 opds.extend(captures);
204 let op = Self {
205 op: Operation::new(ctx, Self::get_concrete_op_info(), out_ty, opds, vec![], 0),
206 };
207 op.set_attr_ssa_matrix_elementwise_closure(ctx, IdentifierAttr::new(closure));
208 op
209 }
210
211 pub fn matrix_in(&self, ctx: &Context) -> Value {
212 self.get_operation().operand(ctx, 0)
213 }
214
215 pub fn closure(&self, ctx: &Context) -> Identifier {
216 let attr = self.get_attr_ssa_matrix_elementwise_closure(ctx).unwrap();
217 attr.clone().into()
218 }
219
220 pub fn closure_captures(&self, ctx: &Context) -> Vec<Value> {
221 self.get_operation().deref(ctx).operands().skip(1).collect()
222 }
223}
224
225impl Printable for ElementwiseOp {
226 fn fmt(
227 &self,
228 ctx: &Context,
229 _state: &printable::State,
230 f: &mut fmt::Formatter<'_>,
231 ) -> fmt::Result {
232 write!(
233 f,
234 "{} = {} ({}) ",
235 self.get_result(ctx).disp(ctx),
236 self.get_opid().disp(ctx),
237 self.matrix_in(ctx).disp(ctx)
238 )?;
239 print_closure(ctx, &self.closure(ctx), &self.closure_captures(ctx), f)
240 }
241}
242impl Parsable for ElementwiseOp {
243 type Arg = Vec<(Identifier, Location)>;
244 type Parsed = OpObj;
245 fn parse<'a>(
246 input: &mut parsable::StateStream<'a>,
247 arg: Self::Arg,
248 ) -> ParseResult<'a, Self::Parsed> {
249 let cur_loc = input.loc();
250
251 spaced(char('(')).parse_stream(input).into_result()?;
252 let mat_in = ssa_opd_parse(input, ())?.0;
253 spaced(char(')')).parse_stream(input).into_result()?;
254
255 let (closure, captures) = parse_closure(input)?.0;
256 let ctx = &mut input.state.ctx;
257
258 if arg.len() != 1 {
259 input_err!(
260 cur_loc,
261 "Expected 1 result, got {} during parsing",
262 arg.len()
263 )?;
264 }
265
266 let op = ElementwiseOp::new(ctx, mat_in, closure, captures);
267 process_parsed_ssa_defs(input, &arg, op.get_operation())?;
268 Ok(OpObj::new(op)).into_parse_result()
269 }
270}
271
272#[op_interface_impl]
273impl MatrixToSSAOp for matrix::ElementwiseOp {
274 fn to_owned_matrix(
275 &self,
276 ctx: &mut Context,
277 rewriter: &mut DialectConversionRewriter,
278 _: &OperandsInfo,
279 ) -> Result<()> {
280 let matrix_in = load_value(self.matrix_in(ctx), ctx, rewriter);
281 let closure = self.closure(ctx);
282 let captures = self.closure_captures(ctx);
283
284 let op = ElementwiseOp::new(ctx, matrix_in, closure, captures);
285 let matrix_out = rewriter.append_op_with_result(ctx, &op);
286 store_value(self.matrix_out(ctx), matrix_out, ctx, rewriter);
287 rewriter.erase_operation(ctx, self.get_operation());
288
289 Ok(())
290 }
291}
292
293fn load_value(ptr: Value, ctx: &mut Context, rewriter: &mut DialectConversionRewriter) -> Value {
294 let load = memory::LoadOp::new(ctx, ptr);
295 rewriter.append_op_with_result(ctx, &load)
296}
297
298fn store_value(
299 ptr: Value,
300 value: Value,
301 ctx: &mut Context,
302 rewriter: &mut DialectConversionRewriter,
303) {
304 let store = memory::StoreOp::new(ctx, ptr, value);
305 rewriter.append_op(ctx, &store);
306}
307
308#[op_interface]
309trait MatrixToSSAOp {
310 verify_op_succ!();
311 fn to_owned_matrix(
312 &self,
313 ctx: &mut Context,
314 rewriter: &mut DialectConversionRewriter,
315 operands_info: &OperandsInfo,
316 ) -> Result<()>;
317}
318
319pub type MatrixToSSAPass = DialectConversionPass<MatrixToSSAConversion>;
320
321#[derive(Default, NamedRewrite)]
322pub struct MatrixToSSAConversion;
323
324impl DialectConversion for MatrixToSSAConversion {
325 fn can_convert_op(&self, ctx: &Context, op: Ptr<Operation>) -> bool {
326 op.impls::<dyn MatrixToSSAOp>(ctx)
327 }
328
329 fn rewrite(
330 &mut self,
331 ctx: &mut Context,
332 rewriter: &mut DialectConversionRewriter,
333 op: Ptr<Operation>,
334 operands_info: &OperandsInfo,
335 ) -> Result<()> {
336 let dyn_op = op.dyn_op(ctx);
337 let to_owned = op_cast::<dyn MatrixToSSAOp>(&*dyn_op).unwrap();
338 to_owned.to_owned_matrix(ctx, rewriter, operands_info)
339 }
340}