1use core::{cell::Ref, fmt};
2
3use alloc::{boxed::Box, vec::Vec};
4
5use derive_more::{From, Into};
6use derive_new::new;
7use num_traits::{AsPrimitive, NumCast};
8use pliron::{
9 builtin::{
10 attr_interfaces::{MaterializableAttr, TypedAttrInterface},
11 attributes::IntegerAttr,
12 ops::ConstantOp,
13 types::IntegerType,
14 },
15 combine::{Parser, parser::char},
16 context::{Context, Ptr},
17 derive::{attr_interface_impl, pliron_attr},
18 irfmt::parsers::{spaced, type_parse},
19 op::Op,
20 operation::Operation,
21 parsable::{IntoParseResult, Parsable, ParseResult, StateStream},
22 printable::{self, Printable},
23 r#type::{TypeHandle, type_impls},
24 utils::apint::{APInt, bw},
25};
26
27use crate::{
28 ConstantValue,
29 apfloat::{APFloat, APFloatType},
30 interfaces::{ConstantAttr, TypedExt, control_flow::SymbolVisibility},
31 settings::Dim3,
32 try_cast_ty,
33 types::scalar::*,
34};
35
36mod entrypoint;
37
38pub use entrypoint::*;
39
40macro_rules! materialize_const {
41 ($ty: ty) => {
42 #[attr_interface_impl]
43 impl MaterializableAttr for $ty {
44 fn materialize(&self, ctx: &mut Context) -> Ptr<Operation> {
45 let const_op = ConstantOp::new(ctx, Box::new(self.clone()));
46 const_op.get_operation()
47 }
48 }
49 };
50}
51
52#[macro_export]
53macro_rules! ext_attribute {
54 ($name: ident: $ty: ty, $($implementors: ty),*) => {
55 paste::paste! {
56 dict_key!([<ATTR_KEY_ $name:upper>], stringify!($name));
57
58 #[op_interface]
59 pub trait [<$name:upper:camel> Interface] {
60 fn [<get_ $name>]<'a>(&self, ctx: &'a pliron::context::Context) -> Option<core::cell::Ref<'a, $ty>> {
61 let self_op = self.get_operation().deref(ctx);
62 Ref::filter_map(self_op, |self_op| {
63 self_op
64 .attributes
65 .get::<$ty>(&[<ATTR_KEY_ $name:upper>])
66 }).ok()
67 }
68
69 fn [<set_ $name>](&self, ctx: &mut Context, value: $ty) {
70 let mut self_op = self.get_operation().deref_mut(ctx);
71 self_op.attributes.set([<ATTR_KEY_ $name:upper>].clone(), value);
72 }
73
74 fn verify(_op: &dyn pliron::op::Op, _ctx: &pliron::context::Context) -> pliron::result::Result<()>
75 where
76 Self: Sized,
77 {
78 Ok(())
79 }
80 }
81 }
82 };
83}
84
85#[macro_export]
86macro_rules! typed_vec_attr {
87 ($ty: ty, $name: literal, $vec_ty: ident) => {
88 #[pliron::derive::def_attribute($name)]
90 #[pliron::derive::format_attribute("`[` vec($0, CharSpace(`,`)) `]`")]
91 #[pliron::derive::verify_succ]
92 #[derive(PartialEq, Eq, Clone, Debug, Hash, Default, derive_more::From)]
93 pub struct $vec_ty(pub Vec<$ty>);
94
95 impl $vec_ty {
96 pub fn new(value: alloc::vec::Vec<$ty>) -> Self {
97 $vec_ty(value)
98 }
99 }
100 };
101}
102#[pliron_attr(name = "cube.zero", format = "`[zero: ` $ty `]`", verifier = "succ")]
106#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
107pub struct ZeroAttr {
108 pub ty: TypeHandle,
109}
110materialize_const!(ZeroAttr);
111
112impl ZeroAttr {
113 pub fn new(ty: impl Into<TypeHandle>) -> Self {
114 Self { ty: ty.into() }
115 }
116}
117
118#[attr_interface_impl]
119impl TypedAttrInterface for ZeroAttr {
120 fn get_type(&self, _ctx: &Context) -> TypeHandle {
121 self.ty
122 }
123}
124
125#[attr_interface_impl]
126impl ConstantAttr for ZeroAttr {
127 fn as_const_val(&self, ctx: &Context) -> ConstantValue {
128 let ty = self.ty.deref(ctx);
129 if type_impls::<dyn APFloatType>(&*ty) {
130 ConstantValue::Float(0.0)
131 } else if self.ty.is_unsigned_int(ctx) || self.ty.is_index(ctx) {
132 ConstantValue::UInt(0)
133 } else if self.ty.is_signed_int(ctx) {
134 ConstantValue::Int(0)
135 } else if self.ty.is_bool(ctx) {
136 ConstantValue::Bool(false)
137 } else {
138 panic!("Invalid value type for `as_const_val`")
139 }
140 }
141 fn as_int(&self, ctx: &Context) -> Option<APInt> {
142 if self.ty.is_int(ctx) || self.ty.is_index(ctx) {
143 Some(APInt::zero(bw(self.ty.size_bits(ctx))))
144 } else {
145 None
146 }
147 }
148 fn float_as_f64(&self, ctx: &Context) -> Option<f64> {
149 let ty = self.ty.deref(ctx);
150 if type_impls::<dyn APFloatType>(&*ty) {
151 Some(0.0)
152 } else {
153 None
154 }
155 }
156}
157
158#[pliron_attr(name = "cube.index", format = "$0", verifier = "succ")]
159#[derive(new, From, PartialEq, Eq, Clone, Copy, Debug, Hash, PartialOrd, Ord)]
160pub struct IndexAttr(pub usize);
161materialize_const!(IndexAttr);
162
163impl IndexAttr {
164 pub fn as_value(&self, _ctx: &Context) -> Option<usize> {
165 Some(self.0)
166 }
167
168 pub fn with_value(&self, _ctx: &Context, new_val: usize) -> Self {
169 Self::new(new_val)
170 }
171}
172
173#[attr_interface_impl]
174impl ConstantAttr for IndexAttr {
175 fn as_const_val(&self, _ctx: &Context) -> ConstantValue {
176 ConstantValue::UInt(self.0 as u64)
177 }
178 fn as_int(&self, ctx: &Context) -> Option<APInt> {
179 let size = IndexType::get(ctx).to_handle().size_bits(ctx);
180 Some(APInt::from_usize(self.0, bw(size)))
181 }
182}
183
184impl From<IndexAttr> for usize {
185 fn from(value: IndexAttr) -> Self {
186 value.0
187 }
188}
189
190#[attr_interface_impl]
191impl TypedAttrInterface for IndexAttr {
192 fn get_type(&self, ctx: &Context) -> TypeHandle {
193 IndexType::get(ctx).into()
194 }
195}
196
197#[pliron_attr(name = "cube.bool", format = "$0", verifier = "succ")]
199#[derive(new, PartialEq, Eq, Clone, Copy, Debug, Hash)]
200pub struct BoolAttr(pub bool);
201materialize_const!(BoolAttr);
202
203impl BoolAttr {
204 pub fn as_value(&self, _ctx: &Context) -> Option<bool> {
205 Some(self.0)
206 }
207
208 pub fn with_value(&self, _ctx: &Context, new_val: bool) -> Self {
209 Self::new(new_val)
210 }
211}
212
213impl From<BoolAttr> for bool {
214 fn from(value: BoolAttr) -> Self {
215 value.0
216 }
217}
218
219impl From<bool> for BoolAttr {
220 fn from(value: bool) -> Self {
221 BoolAttr::new(value)
222 }
223}
224
225impl BoolAttr {
226 pub fn per_lane(
234 ctx: &Context,
235 result: impl pliron::r#type::Typed,
236 value: bool,
237 ) -> Option<Self> {
238 use crate::interfaces::TypedExt;
239 (result.vector_size(ctx) == 1).then(|| Self::new(value))
240 }
241}
242
243#[attr_interface_impl]
244impl TypedAttrInterface for BoolAttr {
245 fn get_type(&self, ctx: &Context) -> TypeHandle {
246 BoolType::get(ctx).into()
247 }
248}
249
250#[attr_interface_impl]
251impl ConstantAttr for BoolAttr {
252 fn as_const_val(&self, _ctx: &Context) -> ConstantValue {
253 ConstantValue::Bool(self.0)
254 }
255}
256
257pub trait IntAttrExt {
258 fn as_value<T>(&self, ctx: &Context) -> Option<T>
259 where
260 T: TypedLiteral + Copy + 'static,
261 i128: AsPrimitive<T>;
262
263 fn with_value<T: NumCast>(&self, ctx: &Context, new_val: T) -> Self;
264}
265
266impl IntAttrExt for IntegerAttr {
267 fn as_value<T>(&self, ctx: &Context) -> Option<T>
268 where
269 T: TypedLiteral + Copy + 'static,
270 i128: AsPrimitive<T>,
271 {
272 if T::is_same_type(ctx, self.get_type().into()) {
273 Some(self.value().to_i128().as_())
274 } else {
275 None
276 }
277 }
278
279 fn with_value<T: NumCast>(&self, ctx: &Context, new_val: T) -> Self {
280 let width = bw(self.get_type().deref(ctx).width() as usize);
281 let val = new_val.to_i128().expect("Should succeed");
282 Self::new(self.get_type(), APInt::from_i128(val, width))
283 }
284}
285
286#[attr_interface_impl]
287impl ConstantAttr for IntegerAttr {
288 fn as_const_val(&self, ctx: &Context) -> ConstantValue {
289 if self.get_type().deref(ctx).is_signed() {
290 ConstantValue::Int(self.value().to_i64())
291 } else {
292 ConstantValue::UInt(self.value().to_u64())
293 }
294 }
295 fn as_int(&self, _ctx: &Context) -> Option<APInt> {
296 Some(self.value())
297 }
298}
299
300typed_vec_attr!(IntegerAttr, "cube.integer_vec", IntegerVecAttr);
301
302#[pliron_attr(name = "cube.float", verifier = "succ")]
303#[derive(new, PartialEq, Clone, Debug, Hash)]
304pub struct FloatAttr {
305 pub ty: TypeHandle,
306 pub val: APFloat,
307}
308materialize_const!(FloatAttr);
309
310impl Printable for FloatAttr {
311 fn fmt(
312 &self,
313 ctx: &Context,
314 state: &printable::State,
315 f: &mut fmt::Formatter<'_>,
316 ) -> fmt::Result {
317 write!(f, "{}: ", self.ty.disp(ctx))?;
318 self.float_type(ctx).disp_value(self.val, ctx, state, f)
319 }
320}
321
322impl Parsable for FloatAttr {
323 type Arg = ();
324 type Parsed = Self;
325
326 fn parse<'a>(input: &mut StateStream<'a>, _: Self::Arg) -> ParseResult<'a, Self::Parsed> {
327 let ty = type_parse(input)?.0;
328 spaced(char::char(':')).parse_stream(input).into_result()?;
329 let ctx = dupe_ref(input.state.ctx);
331 let val = try_cast_ty!(ty.deref(ctx), ctx, dyn APFloatType).parse_value(input)?;
332 Ok(FloatAttr::new(ty, val.0)).into_parse_result()
333 }
334}
335
336fn dupe_ref<'b>(ref_: &Context) -> &'b Context {
337 let ctx: *const Context = ref_;
338 unsafe { &*ctx }
339}
340
341impl FloatAttr {
342 pub fn as_value<T: NumCast + TypedLiteral>(&self, ctx: &Context) -> Option<T> {
343 if T::is_same_type(ctx, self.ty) {
344 Some(T::from(self.float_type(ctx).value_to_f64(self.val)).expect("Should succeed"))
345 } else {
346 None
347 }
348 }
349
350 pub fn with_value<T: NumCast>(&self, ctx: &Context, new_val: T) -> Self {
351 Self::from_f64(ctx, self.ty, new_val.to_f64().expect("Should convert"))
352 }
353
354 pub fn from_f64(ctx: &Context, ty: TypeHandle, val: f64) -> Self {
355 let val = try_cast_ty!(ty.deref(ctx), ctx, dyn APFloatType).value_from_f64(val);
356 Self::new(ty, val)
357 }
358
359 pub fn float_type<'a>(&self, ctx: &'a Context) -> Ref<'a, dyn APFloatType> {
360 Ref::map(self.ty.deref(ctx), |ty| {
361 try_cast_ty!(ty, ctx, dyn APFloatType)
362 })
363 }
364}
365
366#[pliron_attr(name = "cube.complex", verifier = "succ")]
367#[derive(new, PartialEq, Eq, Clone, Copy, Debug, Hash)]
368pub struct ComplexAttr {
369 pub ty: TypeHandle,
370 pub re: APFloat,
371 pub im: APFloat,
372}
373materialize_const!(ComplexAttr);
374
375impl Printable for ComplexAttr {
376 fn fmt(
377 &self,
378 ctx: &Context,
379 state: &printable::State,
380 f: &mut fmt::Formatter<'_>,
381 ) -> fmt::Result {
382 write!(f, "{}, ", self.ty.disp(ctx))?;
383 let float_ty = self.float_type(ctx);
384 float_ty.disp_value(self.re, ctx, state, f)?;
385 write!(f, ", ")?;
386 float_ty.disp_value(self.im, ctx, state, f)
387 }
388}
389
390impl Parsable for ComplexAttr {
391 type Arg = ();
392 type Parsed = Self;
393
394 fn parse<'a>(input: &mut StateStream<'a>, _: Self::Arg) -> ParseResult<'a, Self::Parsed> {
395 let ty = type_parse(input)?.0;
396 spaced(char::char(',')).parse_stream(input).into_result()?;
397 let ctx = dupe_ref(input.state.ctx);
398 let float_ty = Self::float_type_for(ctx, ty);
399 let re = float_ty.parse_value(input)?.0;
400 spaced(char::char(',')).parse_stream(input).into_result()?;
401 let im = float_ty.parse_value(input)?.0;
402 Ok(Self::new(ty, re, im)).into_parse_result()
403 }
404}
405
406impl ComplexAttr {
407 pub fn from_f64(ctx: &Context, ty: TypeHandle, re: f64, im: f64) -> Self {
408 let float_ty = Self::float_type_for(ctx, ty);
409 Self::new(ty, float_ty.value_from_f64(re), float_ty.value_from_f64(im))
410 }
411
412 fn float_type_for(ctx: &Context, ty: TypeHandle) -> Ref<'_, dyn APFloatType> {
413 let ty: TypeHandle = if ty.deref(ctx).is::<Complex32Type>() {
414 Float32Type::get(ctx).into()
415 } else if ty.deref(ctx).is::<Complex64Type>() {
416 Float64Type::get(ctx).into()
417 } else {
418 panic!("expected complex type")
419 };
420 Ref::map(ty.deref(ctx), |ty| try_cast_ty!(ty, ctx, dyn APFloatType))
421 }
422
423 pub fn float_type<'a>(&self, ctx: &'a Context) -> Ref<'a, dyn APFloatType> {
424 Self::float_type_for(ctx, self.ty)
425 }
426}
427
428#[attr_interface_impl]
429impl TypedAttrInterface for ComplexAttr {
430 fn get_type(&self, _ctx: &Context) -> TypeHandle {
431 self.ty
432 }
433}
434
435#[attr_interface_impl]
436impl ConstantAttr for ComplexAttr {
437 fn as_const_val(&self, ctx: &Context) -> ConstantValue {
438 let float_ty = self.float_type(ctx);
439 ConstantValue::Complex(
440 float_ty.value_to_f64(self.re),
441 float_ty.value_to_f64(self.im),
442 )
443 }
444}
445
446#[pliron_attr(name = "cube.dim3", format, verifier = "succ")]
447#[derive(new, From, PartialEq, Clone, Debug, Hash)]
448pub struct Dim3Attr(pub Dim3);
449
450#[attr_interface_impl]
451impl TypedAttrInterface for FloatAttr {
452 fn get_type(&self, _ctx: &Context) -> TypeHandle {
453 self.ty
454 }
455}
456
457#[attr_interface_impl]
458impl ConstantAttr for FloatAttr {
459 fn as_const_val(&self, ctx: &Context) -> ConstantValue {
460 let value = self.float_type(ctx).value_to_f64(self.val);
461 ConstantValue::Float(value)
462 }
463 fn float_as_f64(&self, ctx: &Context) -> Option<f64> {
464 let val = self.float_type(ctx).value_to_f64(self.val);
465 Some(val)
466 }
467}
468
469pub trait TypedLiteral {
470 fn is_same_type(ctx: &Context, ty: TypeHandle) -> bool;
471}
472
473macro_rules! literal {
474 ($ty: ty, $ir_ty: ty, $pred: expr) => {
475 impl TypedLiteral for $ty {
476 fn is_same_type(ctx: &Context, ty: TypeHandle) -> bool {
477 ty.deref(ctx).downcast_ref::<$ir_ty>().is_some_and($pred)
478 }
479 }
480 };
481 ($ty: ty, $ir_ty: ty) => {
482 literal!($ty, $ir_ty, |_| true);
483 };
484}
485
486literal!(usize, IndexType);
487
488literal!(i8, IntegerType, |it| it.width() == 8);
489literal!(i16, IntegerType, |it| it.width() == 16);
490literal!(i32, IntegerType, |it| it.width() == 32);
491literal!(i64, IntegerType, |it| it.width() == 64);
492
493literal!(u8, IntegerType, |it| it.width() == 8);
494literal!(u16, IntegerType, |it| it.width() == 16);
495literal!(u32, IntegerType, |it| it.width() == 32);
496literal!(u64, IntegerType, |it| it.width() == 64);
497
498literal!(half::f16, Float16Type);
499literal!(half::bf16, BFloat16Type);
500literal!(f32, Float32Type);
501literal!(f64, Float64Type);
502
503#[pliron_attr(name = "cube.sym_visibility", format = "$0", verifier = "succ")]
505#[derive(new, PartialEq, Eq, Clone, Copy, Debug, Hash, From, Into)]
506pub struct SymbolVisibilityAttr(pub SymbolVisibility);