use crate::{attributes::*, ops::*};
use pliron::{
attribute::Attribute,
context::{Context, Ptr},
derive::op_interface_impl,
op::Op,
operation::Operation,
printable::State,
result::Result,
};
use pliron_mlir_interop::{ToMlirOp, printers::GenericOp};
use std::fmt;
pub use pliron_mlir_interop::ToMlirOp as MlirOperation;
fn untranslatable(message: String) -> pliron::result::Error {
pliron::input_error_noloc!(pliron_mlir_interop::Error::Untranslatable(message))
}
pub fn property(
ctx: &Context,
key: &str,
attr: &dyn Attribute,
) -> Result<Option<(&'static str, Option<String>)>> {
let wrong = || untranslatable(format!("attribute {key} has an unexpected type"));
Ok(Some(match key {
"arith_fastmath" => {
let flags = attr
.downcast_ref::<FastMathFlagsAttr>()
.ok_or_else(wrong)?
.value;
(
"fastmath",
(!flags.is_none()).then(|| format!("#arith.fastmath<{}>", flags.mlir_spelling())),
)
}
"arith_overflow_flags" => {
let flags = attr
.downcast_ref::<IntegerOverflowFlagsAttr>()
.ok_or_else(wrong)?
.value;
(
"overflowFlags",
(!flags.is_none()).then(|| format!("#arith.overflow<{}>", flags.mlir_spelling())),
)
}
"arith_cmpf_predicate" => {
let value = attr
.downcast_ref::<CmpFPredicateAttr>()
.ok_or_else(wrong)?
.value;
("predicate", Some(format!("{} : i64", u64::from(value))))
}
"arith_cmpi_predicate" => {
let value = attr
.downcast_ref::<CmpIPredicateAttr>()
.ok_or_else(wrong)?
.value;
("predicate", Some(format!("{} : i64", u64::from(value))))
}
"arith_roundingmode" => {
let value = attr
.downcast_ref::<RoundingModeAttr>()
.ok_or_else(wrong)?
.value;
("roundingmode", Some(format!("{} : i32", u64::from(value))))
}
"arith_is_exact" | "arith_nneg" => {
let set = bool::from(
attr.downcast_ref::<pliron::builtin::attributes::BoolAttr>()
.ok_or_else(wrong)?
.clone(),
);
let name = if key == "arith_is_exact" {
"isExact"
} else {
"nonNeg"
};
(name, set.then(|| "unit".to_owned()))
}
"arith_constant_value" => (
"value",
Some(pliron_mlir_builtin::mlir::print_attribute(ctx, attr)?),
),
_ => return Ok(None),
}))
}
pub fn print_generic(
ctx: &Context,
op: Ptr<Operation>,
state: &State,
f: &mut fmt::Formatter<'_>,
translate: &Translate<'_>,
) -> Result<()> {
let name = Operation::get_op_dyn(op, ctx).get_opid().to_string();
print_generic_named(ctx, op, &name, state, f, translate)
}
pub type Translate<'a> =
dyn Fn(&Context, &str, &dyn Attribute) -> Result<Option<(&'static str, Option<String>)>> + 'a;
pub fn print_generic_named(
ctx: &Context,
op: Ptr<Operation>,
name: &str,
state: &State,
f: &mut fmt::Formatter<'_>,
translate: &Translate<'_>,
) -> Result<()> {
Operation::get_op_dyn(op, ctx).verify(ctx)?;
let mut properties = Vec::new();
let mut translated = std::collections::HashSet::new();
{
let raw = op.deref(ctx);
let mut entries = raw.attributes.0.iter().collect::<Vec<_>>();
entries.sort_by(|(a, _), (b, _)| a.as_ref().cmp(b.as_ref()));
for (key, attr) in entries {
if AsRef::<str>::as_ref(key) == "builtin_given_names" {
continue;
}
match translate(ctx, key.as_ref(), &**attr)? {
Some((property, text)) => {
if !translated.insert(property) {
return Err(untranslatable(format!(
"duplicate property {property} on {name}"
)));
}
if let Some(text) = text {
properties.push((property, text));
}
}
None => {
return Err(untranslatable(format!(
"unmapped attribute {key} on {name}"
)));
}
}
}
}
let mut generic = GenericOp::new(op, name);
for (property, text) in properties {
generic = generic.prop_raw(property, text);
}
generic.print(ctx, state, f)
}
fn property_for(
ctx: &Context,
name: &str,
key: &str,
attr: &dyn Attribute,
) -> Result<Option<(&'static str, Option<String>)>> {
let mnemonic = name.strip_prefix("arith.").unwrap_or(name);
let allowed = match key {
"arith_fastmath" => matches!(
mnemonic,
"addf"
| "subf"
| "mulf"
| "divf"
| "remf"
| "negf"
| "flush_denormals"
| "maximumf"
| "minimumf"
| "maxnumf"
| "minnumf"
| "maximumnumf"
| "minimumnumf"
| "cmpf"
| "extf"
| "truncf"
| "convertf"
| "scaling_extf"
| "scaling_truncf"
),
"arith_overflow_flags" => matches!(mnemonic, "addi" | "subi" | "muli" | "shli" | "trunci"),
"arith_roundingmode" => matches!(
mnemonic,
"addf" | "subf" | "mulf" | "divf" | "truncf" | "convertf" | "scaling_truncf"
),
"arith_is_exact" => matches!(mnemonic, "divsi" | "divui" | "shrsi" | "shrui"),
"arith_nneg" => matches!(mnemonic, "extui" | "uitofp" | "index_castui"),
"arith_cmpf_predicate" => mnemonic == "cmpf",
"arith_cmpi_predicate" => mnemonic == "cmpi",
"arith_constant_value" => mnemonic == "constant",
_ => false,
};
if !allowed {
return Err(untranslatable(format!(
"attribute {key} is not a property of {name}"
)));
}
property(ctx, key, attr)
}
macro_rules! operations {
($($ty:ty),* $(,)?) => {$(
#[op_interface_impl]
impl ToMlirOp for $ty {
fn to_mlir(&self, ctx: &Context, state: &State, f: &mut fmt::Formatter<'_>) -> Result<()> {
let name = self.get_opid().to_string();
print_generic(ctx, self.get_operation(), state, f, &|ctx, key, attr| {
property_for(ctx, &name, key, attr)
})
}
}
)*};
}
operations!(
ConstantOp,
AddFOp,
SubFOp,
MulFOp,
DivFOp,
RemFOp,
NegFOp,
FlushDenormalsOp,
MaximumFOp,
MinimumFOp,
MaxNumFOp,
MinNumFOp,
MaximumNumFOp,
MinimumNumFOp,
AddIOp,
AddUIExtendedOp,
SubUIExtendedOp,
MulSIExtendedOp,
MulUIExtendedOp,
SubIOp,
MulIOp,
ShLIOp,
DivSIOp,
DivUIOp,
CeilDivSIOp,
CeilDivUIOp,
FloorDivSIOp,
RemSIOp,
RemUIOp,
AndIOp,
OrIOp,
XOrIOp,
ShRSIOp,
ShRUIOp,
MaxSIOp,
MinSIOp,
MaxUIOp,
MinUIOp,
CmpFOp,
CmpIOp,
SelectOp,
ExtFOp,
TruncFOp,
ConvertFOp,
ScalingExtFOp,
ScalingTruncFOp,
ExtSIOp,
ExtUIOp,
TruncIOp,
SIToFPOp,
UIToFPOp,
FPToSIOp,
FPToUIOp,
IndexCastOp,
IndexCastUIOp,
BitcastOp,
);