use thiserror::Error;
use pliron::{
builtin::{
op_interfaces::{OneResultInterface, SameOperandsAndResultType},
types::{IntegerType, Signedness},
},
context::{Context, Ptr},
decl_op_interface,
error::Result,
location::Located,
op::{op_cast, Op},
operation::Operation,
r#type::{TypeObj, Typed},
use_def_lists::Value,
verify_err,
};
use super::{attributes::IntegerOverflowFlagsAttr, types::PointerType};
#[derive(Error, Debug)]
#[error("Binary Arithmetic Op must have exactly two operands and one result")]
pub struct BinArithOpErr;
decl_op_interface! {
BinArithOp: SameOperandsAndResultType, OneResultInterface {
fn new(ctx: &mut Context, lhs: Value, rhs: Value) -> Self
where
Self: Sized,
{
let op = Operation::new(
ctx,
Self::get_opid_static(),
vec![lhs.get_type(ctx)],
vec![lhs, rhs],
vec![],
0,
);
*Operation::get_op(op, ctx).downcast::<Self>().ok().unwrap()
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let op = op.get_operation().deref(ctx);
if op.get_num_operands() != 2 {
return verify_err!(op.loc(), BinArithOpErr);
}
Ok(())
}
}
}
#[derive(Error, Debug)]
#[error("Integer binary arithmetic Op can only have signless integer result/operand type")]
pub struct IntBinArithOpErr;
decl_op_interface! {
IntBinArithOp: BinArithOp {
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let ty = op_cast::<dyn SameOperandsAndResultType>(op)
.expect("Op must impl SameOperandsAndResultType")
.get_type(ctx)
.deref(ctx);
let Some(int_ty) = ty.downcast_ref::<IntegerType>() else {
return verify_err!(op.get_operation().deref(ctx).loc(), IntBinArithOpErr);
};
if int_ty.get_signedness() != Signedness::Signless {
return verify_err!(op.get_operation().deref(ctx).loc(), IntBinArithOpErr);
}
Ok(())
}
}
}
pub const ATTR_KEY_INTEGER_OVERFLOW_FLAGS: &str = "llvm.integer_overflow_flags";
#[derive(Error, Debug)]
#[error("IntegerOverflowFlag missing on Op")]
pub struct IntBinArithOpWithOverflowFlagErr;
decl_op_interface! {
IntBinArithOpWithOverflowFlag: IntBinArithOp {
fn integer_overflow_flag(&self, ctx: &Context) -> IntegerOverflowFlagsAttr
where
Self: Sized,
{
self.get_operation()
.deref(ctx)
.attributes
.get::<IntegerOverflowFlagsAttr>(ATTR_KEY_INTEGER_OVERFLOW_FLAGS)
.expect("Integer overflow flag missing or is of incorrect type")
.clone()
}
fn set_integer_overflow_flag(&self, ctx: &Context, flag: IntegerOverflowFlagsAttr)
where
Self: Sized,
{
self.get_operation()
.deref_mut(ctx)
.attributes
.set(ATTR_KEY_INTEGER_OVERFLOW_FLAGS, flag);
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let op = op.get_operation().deref(ctx);
if op.attributes.get::<IntegerOverflowFlagsAttr>
(ATTR_KEY_INTEGER_OVERFLOW_FLAGS).is_none()
{
return verify_err!(op.loc(), IntBinArithOpWithOverflowFlagErr);
}
Ok(())
}
}
}
#[derive(Error, Debug)]
#[error("Result must be a pointer type, but is not")]
pub struct PointerTypeResultVerifyErr;
decl_op_interface! {
PointerTypeResult: OneResultInterface {
fn result_pointee_type(&self, ctx: &Context) -> Ptr<TypeObj>;
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
if !op_cast::<dyn OneResultInterface>(op)
.expect("An Op here must impl OneResultInterface")
.result_type(ctx)
.deref(ctx)
.is::<PointerType>()
{
return verify_err!(
op.get_operation().deref(ctx).loc(),
PointerTypeResultVerifyErr
);
}
Ok(())
}
}
}